mail-index-file.c revision f16b058c2ea114f8e6887c06f797e7be6d04d3fb
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2003 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "file-set-size.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "mail-index.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-index-util.h"
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include <unistd.h>
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct mail_index_record *mail_index_next(struct mail_index *index,
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen struct mail_index_record *rec)
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen{
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen i_assert(rec >= INDEX_RECORD_AT(index, 0));
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return rec+1 == INDEX_END_RECORD(index) ? NULL : rec+1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int compress(struct mail_index *index, unsigned int remove_first_idx,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int remove_last_idx)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct mail_index_record *rec = INDEX_RECORD_AT(index, 0);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int idx_limit, count;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen idx_limit = MAIL_INDEX_RECORD_COUNT(index);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen count = remove_last_idx - remove_first_idx + 1;
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen memmove(rec + remove_first_idx, rec + remove_last_idx + 1,
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen (idx_limit - remove_last_idx - 1) * sizeof(*rec));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen index->header->used_file_size -= sizeof(*rec) * count;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen index->mmap_used_length -= sizeof(*rec) * count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* not really needed since append() will initialize it as well,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen but may help preventing problems if change is only partially
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen written to disk */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen memset((char *) rec + index->mmap_used_length, 0, sizeof(*rec) * count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return mail_index_truncate(index);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen}
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenint mail_index_expunge_record_range(struct mail_index *index,
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen struct mail_index_record *first_rec,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen struct mail_index_record *last_rec)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct mail_index_record *rec;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int first_idx, last_idx, idx_limit;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen first_idx = INDEX_RECORD_INDEX(index, first_rec);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen last_idx = INDEX_RECORD_INDEX(index, last_rec);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen idx_limit = MAIL_INDEX_RECORD_COUNT(index);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen i_assert(first_idx <= last_idx);
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen i_assert(last_idx < idx_limit);
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen index->header->messages_count -= last_idx - first_idx + 1;
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen for (rec = first_rec; rec <= last_rec; rec++)
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen mail_index_mark_flag_changes(index, rec, rec->msg_flags, 0);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen return compress(index, first_idx, last_idx);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen}
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstruct mail_index_record *mail_index_lookup(struct mail_index *index,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen unsigned int seq)
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(seq > 0);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (seq > index->header->messages_count)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return NULL;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen return INDEX_RECORD_AT(index, seq-1);
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen}
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstruct mail_index_record *
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainenmail_index_lookup_uid_range(struct mail_index *index, unsigned int first_uid,
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen unsigned int last_uid, unsigned int *seq_r)
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen struct mail_index_record *rec_p;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen unsigned int idx_limit, idx, left_idx, right_idx;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen i_assert(first_uid > 0);
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen i_assert(first_uid <= last_uid);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen rec_p = INDEX_RECORD_AT(index, 0);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen idx_limit = MAIL_INDEX_RECORD_COUNT(index);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen idx = 0;
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen left_idx = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen right_idx = idx_limit;
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen while (left_idx < right_idx) {
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen idx = (left_idx + right_idx) / 2;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (rec_p[idx].uid < first_uid)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen left_idx = idx+1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (rec_p[idx].uid > first_uid)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen right_idx = idx;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (rec_p[idx].uid < first_uid || rec_p[idx].uid > last_uid) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen /* could still be the next one */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen idx++;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (idx == idx_limit ||
c29216637957d4b3126c6929ac5ba98138256ce1Timo Sirainen rec_p[idx].uid < first_uid || rec_p[idx].uid > last_uid) {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (seq_r != NULL) *seq_r = 0;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return NULL;
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (seq_r != NULL)
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen *seq_r = idx + 1;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen return rec_p + idx;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen}
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainenint mail_index_compress(struct mail_index *index)
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen{
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen size_t diff;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen off_t new_file_size;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen if (index->header_size >= sizeof(struct mail_index_header))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen return FALSE;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen /* make sure the file is large enough */
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen diff = sizeof(struct mail_index_header) - index->header_size;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen if (index->mmap_used_length + diff > index->mmap_full_length) {
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen /* mmap_update ftruncates the file to multiples of
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen mail_index_record, make sure we grow it enough here. */
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen new_file_size = index->mmap_used_length + diff +
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen (sizeof(struct mail_index_record) -
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen (diff % sizeof(struct mail_index_record)));
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (file_set_size(index->fd, new_file_size) < 0) {
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen index_set_syscall_error(index, "file_set_size()");
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen return FALSE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen index->header->sync_id++;
4b41116563110d00330896a568eff1078c382827Timo Sirainen if (!mail_index_mmap_update(index))
4b41116563110d00330896a568eff1078c382827Timo Sirainen return FALSE;
4b41116563110d00330896a568eff1078c382827Timo Sirainen }
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen /* if we break, we'll have to rebuild it completely */
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen index->header->flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (!mail_index_fmdatasync(index, index->header_size))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen memmove((char *) index->mmap_base + sizeof(struct mail_index_header),
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen (char *) index->mmap_base + index->header_size,
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen index->mmap_used_length - index->header_size);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen memset((char *) index->mmap_base + index->header_size, 0, diff);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
dbe64f3893616a4005c8946be75d2dc8f6823d72Timo Sirainen index->mmap_used_length += diff;
8a13b020f90e080570658b18c042e7e352c8b14fTimo Sirainen index->header_size = sizeof(struct mail_index_header);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->header->header_size = sizeof(struct mail_index_header);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->header->used_file_size += diff;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->header->sync_id++;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (!mail_index_fmdatasync(index, index->mmap_used_length))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen index->header->flags &= ~MAIL_INDEX_HDR_FLAG_REBUILD;
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen return mail_index_mmap_update(index);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen}
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_index_truncate(struct mail_index *index)
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen{
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen uoff_t empty_space, truncate_threshold;
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (index->mmap_full_length <= INDEX_FILE_MIN_SIZE(index) ||
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen index->anon_mmap)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return TRUE;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen /* really truncate the file only when it's almost empty */
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen empty_space = index->mmap_full_length - index->mmap_used_length;
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen truncate_threshold =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->mmap_full_length / 100 * INDEX_TRUNCATE_PERCENTAGE;
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen if (empty_space > truncate_threshold) {
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen index->mmap_full_length = index->mmap_used_length +
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen (empty_space * INDEX_TRUNCATE_KEEP_PERCENTAGE / 100);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen /* keep the size record-aligned */
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen index->mmap_full_length -= (index->mmap_full_length -
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen index->header_size) %
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sizeof(struct mail_index_record);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (index->mmap_full_length < INDEX_FILE_MIN_SIZE(index))
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->mmap_full_length = INDEX_FILE_MIN_SIZE(index);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ftruncate(index->fd, (off_t)index->mmap_full_length) < 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return index_set_syscall_error(index, "ftruncate()");
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen index->header->sync_id++;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen return TRUE;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen}
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen