PST File Format SDK v0.4
Loading...
Searching...
No Matches
delete.h
Go to the documentation of this file.
1
14
15#ifndef PSTSDK_PST_DELETE_H
16#define PSTSDK_PST_DELETE_H
17
18#include <set>
19#include <vector>
20
21#include "pstsdk/util/errors.h"
23
24#include "pstsdk/ndb/writer.h"
25#include "pstsdk/ltp/propbag.h"
26#include "pstsdk/ltp/table.h"
27#include "pstsdk/ltp/writer.h"
28
29#include "pstsdk/mapitags.h"
30
31namespace pstsdk
32{
33
35
41const slong mapi_message_read = 0x1;
42
53void delete_message(const shared_db_ptr& db, node_id nid);
54
65void delete_attachment(const shared_db_ptr& db, node_id message_nid, node_id attachment_nid);
66
76void delete_folder(const shared_db_ptr& db, node_id nid);
77
86ulonglong wipe_free_space(const shared_db_ptr& db);
87
89
90} // end pstsdk namespace
91
93namespace pstsdk
94{
95namespace detail
96{
97
99inline bool try_read_long(const shared_db_ptr& db, node_id nid, prop_id id, slong& value)
100{
101 try
102 {
103 property_bag bag(db->lookup_node(nid));
104 if(!bag.prop_exists(id))
105 return false;
106
107 value = bag.read_prop<slong>(id);
108 return true;
109 }
110 catch(key_not_found<node_id>&)
111 {
112 return false;
113 }
114}
115
117inline std::vector<row_id> table_row_ids(const shared_db_ptr& db, node_id nid)
118{
119 std::vector<row_id> rows;
120
121 try
122 {
123 table tc(db->lookup_node(nid));
124 for(size_t i = 0; i < tc.size(); ++i)
125 rows.push_back(tc[i].get_row_id());
126 }
127 catch(key_not_found<node_id>&) { }
128
129 return rows;
130}
131
132inline std::vector<row_id> table_row_ids(const shared_db_ptr& db, node_id owner, node_id sub)
133{
134 std::vector<row_id> rows;
135
136 table tc(db->lookup_node(owner).lookup(sub));
137 for(size_t i = 0; i < tc.size(); ++i)
138 rows.push_back(tc[i].get_row_id());
139
140 return rows;
141}
142
146inline std::vector<node_id> search_contents_tables(const shared_db_ptr& db)
147{
148 std::vector<node_id> tables;
149 std::shared_ptr<nbt_page> root = db->read_nbt_root();
150
151 for(const_nodeinfo_iterator i = root->begin(); i != root->end(); ++i)
152 {
154 continue;
155
157 continue;
158
159 tables.push_back((*i).id);
160 }
161
162 return tables;
163}
164
165template<typename T>
166inline void sweep_search_folders(db_writer<T>& writer, const std::vector<node_id>& tables,
167 node_id message)
168{
169 for(size_t i = 0; i < tables.size(); ++i)
170 {
171 try { tc_remove_row(writer, tables[i], message); }
172 catch(key_not_found<row_id>&) { }
173 }
174}
175
176inline node_id folder_table(node_id folder, nid_type type)
177{
178 return make_nid(type, get_nid_index(folder));
179}
180
182template<typename T>
183inline bool remove_message_row(db_writer<T>& writer, node_id folder, node_id message, bool& associated)
184{
185 try
186 {
187 tc_remove_row(writer, folder_table(folder, nid_type_contents_table), message);
188 associated = false;
189 return true;
190 }
191 catch(key_not_found<row_id>&) { }
192
193 try
194 {
195 tc_remove_row(writer, folder_table(folder, nid_type_associated_contents_table), message);
196 associated = true;
197 return true;
198 }
199 catch(key_not_found<row_id>&) { }
200
201 return false;
202}
203
204template<typename T>
205inline void delete_message_impl(const std::shared_ptr<database_impl<T> >& db, node_id nid)
206{
207 const node_info info = db->lookup_node_info(nid);
208 const node_id folder = info.parent_id;
209
210 // Everything the counts depend on has to be read before the first write,
211 // because a write invalidates the store's cached tree.
212 slong flags = 0;
213 const bool has_flags = try_read_long(db, nid, PR_MESSAGE_FLAGS, flags);
214 const bool unread = has_flags && (flags & mapi_message_read) == 0;
215
216 slong content_count = 0;
217 slong unread_count = 0;
218 slong associated_count = 0;
219 const bool has_content = try_read_long(db, folder, PR_CONTENT_COUNT, content_count);
220 const bool has_unread = try_read_long(db, folder, PR_CONTENT_UNREAD, unread_count);
221 const bool has_associated = try_read_long(db, folder, PR_ASSOC_CONTENT_COUNT, associated_count);
222
223 const std::vector<node_id> search = search_contents_tables(db);
224
225 db_writer<T> writer(db);
226
227 bool associated = false;
228 const bool removed = folder != 0 && remove_message_row(writer, folder, nid, associated);
229
230 if(removed && associated && has_associated && associated_count > 0)
231 pc_set_inline(writer, folder, (prop_id)PR_ASSOC_CONTENT_COUNT, associated_count - 1);
232
233 if(removed && !associated && has_content && content_count > 0)
234 pc_set_inline(writer, folder, (prop_id)PR_CONTENT_COUNT, content_count - 1);
235
236 if(removed && !associated && unread && has_unread && unread_count > 0)
237 pc_set_inline(writer, folder, (prop_id)PR_CONTENT_UNREAD, unread_count - 1);
238
239 sweep_search_folders(writer, search, nid);
240
241 writer.delete_node(nid);
242 writer.commit();
243}
244
245template<typename T>
246inline void delete_attachment_impl(const std::shared_ptr<database_impl<T> >& db,
247 node_id message_nid, node_id attachment_nid)
248{
249 db_writer<T> writer(db);
250 const typename db_writer<T>::data_ref message = writer.node_ref(message_nid);
251
252 if(message.sub == 0)
253 throw key_not_found<node_id>(attachment_nid);
254
255 const typename db_writer<T>::data_ref table =
256 writer.subnode_ref(message.sub, nid_attachment_table);
257
258 tc_remove_row(writer, table, attachment_nid);
259 writer.subnode_remove(message.sub, attachment_nid);
260
261 // the table survives with no rows, the way a message that never had an
262 // attachment carries one, so the flag is what a client actually reads
263 size_t left = 0;
264 try { left = table_row_ids(db, message_nid, nid_attachment_table).size(); }
265 catch(key_not_found<node_id>&) { }
266
267 if(left == 0)
268 {
269 try { pc_set_inline(writer, message_nid, (prop_id)PR_HASATTACH, 0); }
270 catch(key_not_found<prop_id>&) { }
271 }
272
273 writer.commit();
274}
275
276template<typename T>
277inline ulonglong wipe_impl(const std::shared_ptr<database_impl<T> >& db)
278{
279 db_writer<T> writer(db);
280 const ulonglong wiped = writer.wipe_free_space();
281 writer.commit();
282 return wiped;
283}
284
285template<typename T>
286inline void delete_folder_contents(db_writer<T>& writer, const std::shared_ptr<database_impl<T> >& db,
287 node_id folder, const std::vector<node_id>& search,
288 std::set<node_id>& seen)
289{
290 // a hierarchy table naming an ancestor would otherwise recurse forever
291 if(!seen.insert(folder).second)
292 return;
293
294 const std::vector<row_id> subfolders =
295 table_row_ids(db, folder_table(folder, nid_type_hierarchy_table));
296
297 for(size_t i = 0; i < subfolders.size(); ++i)
298 delete_folder_contents(writer, db, subfolders[i], search, seen);
299
300 // The tables go with the folder, so the messages only need their nodes
301 // unlinked; there is no row left to take them out of.
302 const std::vector<row_id> messages =
303 table_row_ids(db, folder_table(folder, nid_type_contents_table));
304 const std::vector<row_id> associated =
305 table_row_ids(db, folder_table(folder, nid_type_associated_contents_table));
306
307 for(size_t i = 0; i < messages.size(); ++i)
308 {
309 sweep_search_folders(writer, search, (node_id)messages[i]);
310 try { writer.delete_node(messages[i]); }
311 catch(key_not_found<node_id>&) { }
312 }
313
314 for(size_t i = 0; i < associated.size(); ++i)
315 {
316 try { writer.delete_node(associated[i]); }
317 catch(key_not_found<node_id>&) { }
318 }
319
322
323 for(size_t i = 0; i < sizeof(tables) / sizeof(tables[0]); ++i)
324 {
325 try { writer.delete_node(folder_table(folder, tables[i])); }
326 catch(key_not_found<node_id>&) { }
327 }
328
329 try { writer.delete_node(folder); }
330 catch(key_not_found<node_id>&) { }
331}
332
333template<typename T>
334inline void delete_folder_impl(const std::shared_ptr<database_impl<T> >& db, node_id nid)
335{
336 const node_info info = db->lookup_node_info(nid);
337 const node_id parent = info.parent_id;
338
339 const size_t siblings =
340 table_row_ids(db, folder_table(parent, nid_type_hierarchy_table)).size();
341 const std::vector<node_id> search = search_contents_tables(db);
342
343 db_writer<T> writer(db);
344
345 if(parent != 0)
346 {
347 try { tc_remove_row(writer, folder_table(parent, nid_type_hierarchy_table), nid); }
348 catch(key_not_found<row_id>&) { }
349
350 // the expander in a client reads this flag rather than counting rows
351 if(siblings == 1)
352 {
353 try { pc_set_inline(writer, parent, (prop_id)PR_SUBFOLDERS, 0); }
354 catch(key_not_found<prop_id>&) { }
355 }
356 }
357
358 std::set<node_id> seen;
359 delete_folder_contents(writer, db, nid, search, seen);
360 writer.commit();
361}
362
363} // end detail namespace
364} // end pstsdk namespace
365
366inline void pstsdk::delete_message(const shared_db_ptr& db, node_id nid)
367{
368 if(std::shared_ptr<large_pst> unicode = std::dynamic_pointer_cast<large_pst>(db))
369 return detail::delete_message_impl(unicode, nid);
370 if(std::shared_ptr<small_pst> ansi = std::dynamic_pointer_cast<small_pst>(db))
371 return detail::delete_message_impl(ansi, nid);
372
373 throw invalid_format();
374}
375
376inline void pstsdk::delete_attachment(const shared_db_ptr& db, node_id message_nid,
377 node_id attachment_nid)
378{
379 if(std::shared_ptr<large_pst> unicode = std::dynamic_pointer_cast<large_pst>(db))
380 return detail::delete_attachment_impl(unicode, message_nid, attachment_nid);
381 if(std::shared_ptr<small_pst> ansi = std::dynamic_pointer_cast<small_pst>(db))
382 return detail::delete_attachment_impl(ansi, message_nid, attachment_nid);
383
384 throw invalid_format();
385}
386
387inline pstsdk::ulonglong pstsdk::wipe_free_space(const shared_db_ptr& db)
388{
389 if(std::shared_ptr<large_pst> unicode = std::dynamic_pointer_cast<large_pst>(db))
390 return detail::wipe_impl(unicode);
391 if(std::shared_ptr<small_pst> ansi = std::dynamic_pointer_cast<small_pst>(db))
392 return detail::wipe_impl(ansi);
393
394 throw invalid_format();
395}
396
397inline void pstsdk::delete_folder(const shared_db_ptr& db, node_id nid)
398{
399 if(std::shared_ptr<large_pst> unicode = std::dynamic_pointer_cast<large_pst>(db))
400 return detail::delete_folder_impl(unicode, nid);
401 if(std::shared_ptr<small_pst> ansi = std::dynamic_pointer_cast<small_pst>(db))
402 return detail::delete_folder_impl(ansi, nid);
403
404 throw invalid_format();
405}
407
408#endif
Contains references to other bth_node allocations.
Definition heap.h:364
const_iterator begin() const
Returns a STL style iterator positioned at the first entry.
Definition btree.h:85
The exceptions used by pstsdk.
boost::uint64_t ulonglong
Definition primitives.h:70
ulong get_nid_index(node_id id)
Get a node index from a node id.
Definition primitives.h:231
nid_type
Different node types found in a PST file.
Definition primitives.h:120
nid_type get_nid_type(node_id id)
Get a node type from a node id.
Definition primitives.h:223
ulong node_id
Definition primitives.h:86
boost::int32_t slong
Definition primitives.h:69
#define make_nid(nid_type, nid_index)
Construct a node_id (NID) from a node type and index.
Definition primitives.h:160
ushort prop_id
Definition primitives.h:93
@ nid_type_hierarchy_table
Definition primitives.h:134
@ nid_type_contents_table
Definition primitives.h:135
@ nid_type_search_contents_table
Definition primitives.h:137
@ nid_type_associated_contents_table
Definition primitives.h:136
@ nid_all_message_search_contents
Definition primitives.h:202
@ nid_attachment_table
Definition primitives.h:196
In place edits of heaps, BTHs, property contexts and table contexts.
#define PR_CONTENT_UNREAD
Definition mapitags.h:433
#define PR_MESSAGE_FLAGS
Definition mapitags.h:311
#define PR_HASATTACH
Definition mapitags.h:330
#define PR_SUBFOLDERS
Definition mapitags.h:438
#define PR_CONTENT_COUNT
Definition mapitags.h:432
#define PR_ASSOC_CONTENT_COUNT
Definition mapitags.h:455
Contains the definition of all in memory representations of disk structures.
Definition disk.h:19
std::shared_ptr< db_context > shared_db_ptr
const_btree_node_iter< node_id, node_info > const_nodeinfo_iterator
In place edits of an open store.
Primitive structures defined by MS-PST and MAPI.
Property Bag (or Property Context, or PC) implementation.
Table (or Table Context, or TC) implementation.