1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
18 #include "eventloop.h"
19 #include "allocator.h"
20 #include "integerset.h"
21 #include "estringlist.h"
22 #include "transaction.h"
25 static Map<Mailbox> * mailboxes = 0;
26 static UDict<Mailbox> * mailboxesByName = 0;
27 static bool wiped = false;
35 : type( Mailbox::Ordinary ), id( 0 ),
36 uidnext( 0 ), uidvalidity( 0 ), owner( 0 ),
37 parent( 0 ), children( 0 ),
51 List< Mailbox > * children;
57 /*! \class Mailbox mailbox.h
58 This class represents a node in the global mailbox hierarchy.
60 Every Mailbox has a unique name() within the hierarchy. Any
61 Mailbox that can contain messages has a non-zero numeric id() and
62 attributes like uidvalidity() and uidnext(). Mailboxes have a
63 parent() and may have a number of children().
65 Some mailboxes aren't quite real. A Mailbox can be deleted(), in
66 which case it can contain no messags. If recreated, a deleted()
67 mailbox preserves its uidvalidity() and uid series.
69 This class maintains a tree of mailboxes, based on the contents of
70 the mailboxes table and descriptive messages from the OCServer. It
71 can find() a named mailbox in this hierarchy.
83 MailboxReader( EventHandler * ev, int64 );
88 static List<MailboxReader> * readers = 0;
91 MailboxReader::MailboxReader( EventHandler * ev, int64 c )
92 : owner( ev ), q( 0 ), done( false )
95 ::readers = new List<MailboxReader>;
96 Allocator::addEternal( ::readers, "active mailbox readers" );
98 ::readers->append( this );
99 q = new Query( "select m.id, m.name, m.deleted, m.owner, "
100 "m.uidnext, m.nextmodseq, m.uidvalidity "
101 //"m.change " // better: m.change
105 c = c; //query->bind( 1, c );
111 void MailboxReader::execute() {
112 while ( q->hasResults() ) {
113 Row * r = q->nextRow();
115 UString n = r->getUString( "name" );
116 uint id = r->getInt( "id" );
117 Mailbox * m = ::mailboxes->find( id );
118 if ( !m || m->name() != n ) {
119 m = Mailbox::obtain( n );
120 if ( n != m->d->name )
123 ::mailboxes->insert( id, m );
126 if ( r->getBoolean( "deleted" ) )
127 m->setType( Mailbox::Deleted );
129 m->setType( Mailbox::Ordinary );
131 uint uidvalidity = r->getInt( "uidvalidity" );
132 if ( m->d->uidvalidity != uidvalidity ) {
133 m->d->uidvalidity = uidvalidity;
136 if ( !r->isNull( "owner" ) )
137 m->setOwner( r->getInt( "owner" ) );
139 m->setUidnextAndNextModSeq( r->getInt( "uidnext" ),
140 r->getBigint( "nextmodseq" ),
144 if ( !q->done() || done )
148 if ( q->transaction() )
149 q->transaction()->commit();
150 ::readers->remove( this );
152 if ( q->failed() && !EventLoop::global()->inShutdown() ) {
153 List<Mailbox> * c = Mailbox::root()->children();
154 if ( c && !c->isEmpty() )
155 log( "Couldn't update mailbox tree: " + q->error() );
157 log( "Couldn't create mailbox tree: " + q->error(),
165 class MailboxesWatcher
166 : public EventHandler
169 MailboxesWatcher(): EventHandler(), t( 0 ), m( 0 ) {
170 (void)new DatabaseSignal( "mailboxes_updated", this );
173 if ( EventLoop::global()->inShutdown() )
177 // use a timer to run only one mailboxreader per 2-3
179 t = new Timer( this, 2 );
181 else if ( t->active() ) {
182 // the timer is already running, so ignore this
184 else if ( m && !m->done ) {
185 // a mailboxreader is working, and one is enough, so try
187 t = new Timer( this, 2 );
190 // time's out, time to work
192 m = new MailboxReader( 0, 0 );
201 // this helper class is used to recover when testing tools
202 // violate various database invariants.
203 class MailboxObliterator
204 : public EventHandler
208 MailboxObliterator(): EventHandler(), mr( 0 ) {
210 (void)new DatabaseSignal( "obliterated", this );
214 ::mailboxes->clear();
215 ::mailboxesByName->clear();
217 (void)Mailbox::root();
218 mr = new MailboxReader( this, 0 );
230 /*! This static function is responsible for building a tree of
231 Mailboxes from the contents of the mailboxes table. It expects to
232 be called by ::main().
234 The \a owner (if one is specified) is notified of completion.
237 void Mailbox::setup( EventHandler * owner )
241 ::mailboxes = new Map<Mailbox>;
242 Allocator::addEternal( ::mailboxes, "mailbox tree" );
244 ::mailboxesByName = new UDict<Mailbox>;
245 Allocator::addEternal( ::mailboxesByName, "mailbox tree" );
250 (new MailboxReader( owner, 0 ))->q->execute();
252 (void)new MailboxesWatcher;
253 if ( !Configuration::toggle( Configuration::Security ) )
254 (void)new MailboxObliterator;
258 /*! Creates a Mailbox named \a name. This constructor is only meant to
259 be used via Mailbox::obtain(). */
261 Mailbox::Mailbox( const UString &name )
262 : d( new MailboxData )
268 /*! Returns the fully qualified name of this Mailbox. */
270 UString Mailbox::name() const
276 /*! Sets the type of this Mailbox to \a t. The initial value is
277 Ordinary (because it has to be something).
280 void Mailbox::setType( Type t )
288 /*! Returns the type of this Mailbox. May be Ordinary, Deleted, or
292 Mailbox::Type Mailbox::type() const
298 /*! Returns the database ID of this Mailbox.
301 uint Mailbox::id() const
307 /*! Notifies this Mailbox that its database ID is \a i.
310 void Mailbox::setId( uint i ) const
316 /*! Returns the next UID value that will be used for this mailbox. */
318 uint Mailbox::uidnext() const
324 /*! Notifies this Mailbox that its correct uidvalidity is \a i. Should
325 generally not be called.
328 void Mailbox::setUidvalidity( uint i )
334 /*! Returns the UIDVALIDITY value of this Mailbox. This never changes. */
336 uint Mailbox::uidvalidity() const
338 return d->uidvalidity;
342 /*! Returns true if this mailbox isn't "special". */
344 bool Mailbox::ordinary() const
346 return d->type == Ordinary;
350 /*! Returns true if this mailbox is currently deleted. */
352 bool Mailbox::deleted() const
354 return d->type == Deleted;
358 /*! Returns true if this Mailbox represents a user's "home directory",
362 bool Mailbox::isHome() const
366 if ( d->owner && !d->parent->d->owner )
372 /*! Returns the numeric user id of the owner of this mailbox, or 0 if
373 the mailbox has no defined owner (or is not yet known to have one).
376 uint Mailbox::owner() const
382 /*! Returns a pointer to the parent of this Mailbox, or 0 if it is the
386 Mailbox *Mailbox::parent() const
392 /*! Returns a pointer to a List of this Mailbox's children, or 0 if it
396 List< Mailbox >* Mailbox::children() const
402 /*! Returns true if this mailbox has at least one real, existing child
403 mailbox, including indirect children, and false if not.
406 bool Mailbox::hasChildren() const
408 List<Mailbox>::Iterator it( d->children );
410 if ( !it->deleted() )
412 if ( it->hasChildren() )
420 /*! Returns a pointer to the Mailbox object at the root of the global
424 Mailbox * Mailbox::root()
428 return Mailbox::obtain( r, true );
432 /*! Returns a pointer to the Mailbox with \a id, or a null pointer if
433 there is no such (known) Mailbox.
435 Deleted mailboxes are included in the search.
438 Mailbox * Mailbox::find( uint id )
442 return ::mailboxes->find( id );
446 /*! Returns a pointer to a Mailbox named \a name, or 0 if the named
447 mailbox doesn't exist. If \a deleted is true, deleted mailboxes
448 are included in the search. The \a name must be fully-qualified.
451 Mailbox * Mailbox::find( const UString &name, bool deleted )
453 Mailbox * m = obtain( name, false );
456 if ( m->deleted() && !deleted )
462 /*! Returns a pointer to the closest existing parent mailbox for \a
463 name, or a null pointer if \a name doesn't look like a mailbox
464 name at all, or if no parent mailboxes of \a name exist.
467 Mailbox * Mailbox::closestParent( const UString & name )
469 if ( name[0] != '/' )
474 uint i = n.length() - 1;
477 while ( i > 0 && n[i] != '/' )
482 Mailbox * m = obtain( n );
493 /*! Obtain a mailbox with \a name, creating Mailbox objects as
494 necessary and permitted.
496 if \a create is true (this is the default) and there is no such
497 Mailbox, obtain() creates one, including any necessary parents.
499 If \a create is false and there is no such Mailbox, obtain()
500 returns null without creating anything.
503 Mailbox * Mailbox::obtain( const UString & name, bool create )
505 if ( name[0] != '/' )
511 UString n = name.titlecased();
512 Mailbox * m = ::mailboxesByName->find( n );
517 while ( i <= n.length() ) {
518 if ( i >= n.length() || n[i] == '/' ) {
522 m = ::mailboxesByName->find( n.mid( 0, l ) );
524 m = new Mailbox( name.mid( 0, l ) );
525 ::mailboxesByName->insert( n.mid( 0, l ), m );
527 if ( !p->d->children )
528 p->d->children = new List<Mailbox>;
529 p->d->children->append( m );
541 /*! Sets this Mailbox's owner to \a n (which is assumed to be a valid
545 void Mailbox::setOwner( uint n )
551 /*! Atomically sets both uidnext() to \a n and nextModSeq() to \a
552 m. Uses subtransactions of \a t for all work needed.
555 void Mailbox::setUidnextAndNextModSeq( uint n, int64 m, Transaction * t )
557 if ( n == d->uidnext && m == d->nextModSeq )
562 (void)new SessionInitialiser( this, t );
566 /*! Changes this Mailbox's deletedness to \a del. */
568 void Mailbox::setDeleted( bool del )
577 /*! If this Mailbox does not exist, this function enqueues a Query to
578 create it in the Transaction \a t and returns the Query. Otherwise
579 it returns 0 and does nothing. It does not commit the transaction.
581 If \a owner is non-null, the new mailbox is owned by by \a owner.
584 Query * Mailbox::create( Transaction * t, User * owner )
589 q = new Query( "update mailboxes "
590 "set deleted='f',owner=$2,first_recent=uidnext "
594 else if ( id() == 0 ) {
595 q = new Query( "insert into mailboxes "
596 "(name,owner,uidnext,uidvalidity,deleted) "
597 "values ($1,$2,1,1,'f')", 0 );
598 q->bind( 1, name() );
605 q->bind( 2, owner->id() );
611 // We assume that the caller has checked permissions.
613 Mailbox * m = parent();
616 if ( m->deleted() ) {
617 // Should we leave it be or undelete it?
619 else if ( m->id() == 0 ) {
620 q = new Query( "insert into mailboxes "
621 "(name,owner,uidnext,uidvalidity,deleted) "
622 "values ($1,null,1,1,'f')", 0 );
623 q->bind( 1, m->name() );
626 // We rely on the trigger to set the ownership of the
627 // intermediate mailboxes being created.
635 t->enqueue( new Query( "notify mailboxes_updated", 0 ) );
641 /*! If this Mailbox can be deleted, this function enqueues a Query to do
642 so in the Transaction \a t and returns the Query. If not, it returns
643 0 and does nothing. It does not commit the transaction.
646 Query * Mailbox::remove( Transaction * t )
652 new Query( "update mailboxes set deleted='t',owner=null "
657 q = new Query( "delete from permissions where mailbox=$1", 0 );
661 t->enqueue( new Query( "notify mailboxes_updated", 0 ) );
667 /*! Adds one or more queries to \a t, to ensure that the Mailbox tree
668 is up to date when \a t is commited.
671 void Mailbox::refreshMailboxes( class Transaction * t )
674 MailboxReader * mr = new MailboxReader( 0, 0 );
675 Transaction * s = t->subTransaction( mr );
677 s->enqueue( new Query( "notify mailboxes_updated", 0 ) );
682 /*! Returns a pointer to the sessions on this mailbox. The return
683 value may be a null pointer. In the event of client/network
684 problems it may also include sessions that have recently become
688 List<Session> * Mailbox::sessions() const
690 List<Session> * r = 0;
692 List<Connection> * connections = EventLoop::global()->connections();
693 List<Connection>::Iterator i( connections );
695 Session * s = i->session();
696 if ( s && s->mailbox() == this ) {
698 r = new List<Session>;
707 /*! Returns the value last specified by nextModSeq(), or 1 initially. */
709 int64 Mailbox::nextModSeq() const
711 return d->nextModSeq;
715 /*! Returns true if \a s is syntactically valid as a mailbox name, and
716 false if not. Empty names are invalid, ones that do not start with
717 '/' are too, etc, etc.
719 Notably, the root ("/") is not valid. This is a borderline case -
720 for example "/" is valid as parent for creating new mailboxes, but
721 not as name of a new mailbox.
724 bool Mailbox::validName( const UString & s )
726 if ( !s.startsWith( "/" ) )
728 if ( s.endsWith( "/" ) )
730 if ( s.contains( "//" ) )
736 /*! This extremely slow pattern matching helper checks that \a pattern
737 (starting at character \a p) matches \a name (starting at
738 character \a n), and returns 2 in case of match, 1 if a child of
739 \a name might match, and 0 if neither is the case.
741 There are only two wildcards: * matches zero or more characters. %
742 matches zero or more characters, but does not match /.
744 Note that this match is case sensitive. Our mailbox names are case
745 insensitive, so the caller typically has to call
746 UString::titlecased() on both arguments.
749 uint Mailbox::match( const UString & pattern, uint p,
750 const UString & name, uint n )
753 while ( p <= pattern.length() ) {
754 if ( pattern[p] == '*' || pattern[p] == '%' ) {
756 while ( pattern[p] == '*' || pattern[p] == '%' ) {
757 if ( pattern[p] == '*' )
765 while ( i < name.length() && name[i] != '/' )
767 while ( i >= n && i <= name.length() ) {
768 uint s = match( pattern, p, name, i );
771 if ( s == 1 || star )
776 else if ( p == pattern.length() && n == name.length() ) {
777 // ran out of pattern and name at the same time. success.
780 else if ( pattern[p] == name[n] ) {
784 else if ( pattern[p] == '/' && n >= name.length() ) {
785 // we ran out of name and the pattern wants a child.
789 // plain old mismatch.
798 /*! Returns true if the Mailbox subsystem is currently in the process
799 of relearning all the Mailbox objects from the database. Never
800 returns true during normal operations, but may if if the database
801 is wiped out by a test rig.
804 bool Mailbox::refreshing()
810 if ( ::readers->isEmpty() )
816 /*! Aborts all sessions on this Mailbox. Good to call when a mailbox
820 void Mailbox::abortSessions()
822 List<Connection> * connections = EventLoop::global()->connections();
823 List<Connection>::Iterator i( connections );
825 Session * s = i->session();
826 if ( s && s->mailbox() == this ) {