Exemple #1
1
IntegerSet IntegerSet::set_union(const IntegerSet& o){
    IntegerSet out;
    for(int i = 0; i < num_elements; i++){
        out.add(elements[i]);
    }
    for(int i = 0; i < o.num_elements; i++){
        out.add(o.elements[i]);
    }
    return out;
}
Exemple #2
0
IntegerSet Command::set( bool parseMsns = false )
{
    IntegerSet result;
    ImapSession *s = 0;
    if ( imap() )
        s = imap()->session();

    uint n1 = 0, n2 = 0;
    bool done = false;
    while ( ok() && !done ) {
        char c = nextChar();
        if ( c == '*' ) {
            step();
            n1 = 0;
            if ( s )
                n1 = s->largestUid();
            else
                error( Bad, "Need a mailbox session to use * as an UID/MSN" );
        }
        else if ( c >= '1' && c <= '9' ) {
            if ( parseMsns )
                n1 = msn();
            else
                n1 = nzNumber();
        }
        else {
            error( Bad, "number or '*' expected, saw: " + following() );
        }
        c = nextChar();
        if ( c == ':' ) {
            if ( n2 )
                error( Bad,
                       "saw colon after range (" + fn( n1 ) + ":" +
                       fn( n2 ) + "), saw:" + following() );
            n2 = n1;
            n1 = 0;
            step();
        }
        else if ( ok() ) {
            if ( n2 )
                result.add( n1, n2 );
            else
                result.add( n1 );
            n1 = 0;
            n2 = 0;
            if ( c == ',' )
                step();
            else
                done = true;
        }
    };
    return result;
}
Exemple #3
0
void ImapSession::emitUpdates( Transaction * t )
{
    if ( d->emitting )
        return;
    d->emitting = true;
    bool work = false;

    Scope x( d->l );

    IntegerSet e;
    e.add( expunged() );
    e.remove( d->expungesReported );
    if ( !e.isEmpty() ) {
        d->expungesReported.add( e );
        while ( !e.isEmpty() ) {
            (void)new ImapExpungeResponse( e.smallest(), this );
            work = true;
            e.remove( e.smallest() );
        }
    }

    emitFlagUpdates( t );

    if ( d->uidnext < uidnext() ) {
        if ( !d->existsResponse ) {
            d->existsResponse =
                new ImapSessionData::ExistsResponse( this, d );
            work = true;
        }
        if ( !d->recentResponse ) {
            d->recentResponse =
                new ImapSessionData::RecentResponse( this, d );
            work = true;
        }
        if ( !d->uidnextResponse ) {
            d->uidnextResponse =
                new ImapSessionData::UidnextResponse( this, d );
            work = true;
        }
    }

    if ( d->nms < nextModSeq() )
        d->nms = nextModSeq();
    if ( d->changed.isEmpty() )
        d->cms = d->nms;

    if ( work )
        d->i->unblockCommands();
    d->i->emitResponses();

    d->emitting = false;
}
Exemple #4
0
IntegerSet IntegerSet::set_difference(const IntegerSet& o){
    IntegerSet out;
//    for(int i = 0; i < o.num_elements; i++){
//        if(!contains(o.elements[i])){
//            out.add(o.elements[i]);
//        }
//    }
    for(int i = 0; i < num_elements; i++){
        if(!o.contains(elements[i])){
            out.add(elements[i]);
        }
    }
    return out;
}
Exemple #5
0
void MaildirMailbox::readSubDir( const EString & sub )
{
    DIR * dir = opendir( ( d->path + "/" + sub ).cstr() );
    if ( !dir )
        return;

    Map<EStringList> files;
    IntegerSet times;

    struct dirent * de = readdir( dir );
    while ( de ) {
        if ( de->d_name[0] >= '0' && de->d_name[0] <= '9' ) {
            EString n( de->d_name );
            int i = n.find( '.' );
            bool ok = false;
            if ( i > 0 )
                i = n.mid( 0, i ).number( &ok );
            if ( ok ) {
                EString * f = new EString ( sub );
                f->append( "/" );
                f->append( n );
                EStringList * l = 0;
                l = files.find( i );
                if ( !l ) {
                    l = new EStringList;
                    files.insert( i, l );
                    times.add( i ); // XXX: how very evil
                }
                l->append( f );
            }
            else {
                // no dot in the name... what is it?
            }
        }
        de = readdir( dir );
    }
    closedir( dir );

    while ( !times.isEmpty() ) {
        uint n = times.smallest();
        times.remove( n );
        EStringList * l = files.find( n );
        EStringList::Iterator i( *l );
        while ( i ) {
            d->messages.append( *i );
            ++i;
        }
    }
}
Exemple #6
0
void SessionInitialiser::recordExpunges()
{
    if ( !d->expunges )
        return;
    Row * r = 0;
    IntegerSet uids;
    while ( (r=d->expunges->nextRow()) != 0 )
        uids.add( r->getInt( "uid" ) );
    if ( uids.isEmpty() )
        return;

    List<Session>::Iterator i( d->sessions );
    while ( i ) {
        Session * s = i;
        ++i;
        s->expunge( uids );
    }
}
Exemple #7
0
void Select::execute()
{
    if ( state() != Executing )
        return;

    if ( Flag::id( "\\Deleted" ) == 0 ) {
        // should only happen when we flush the entire database during
        // testing, so we don't bother being accurate or fast, but
        // simply try again in a second.
        (void)new Timer( this, 1 );
        return;
    }

    if ( !d->permissions ) {
        if ( d->condstore )
            imap()->setClientSupports( IMAP::Condstore );
        if ( d->annotate )
            imap()->setClientSupports( IMAP::Annotate );
        if ( d->mailbox->deleted() )
            error( No, d->mailbox->name().ascii() + " is deleted" );

        if ( !ok() ) {
            finish();
            return;
        }

        d->permissions = new Permissions( d->mailbox, imap()->user(),
                                          this );
    }

    if ( d->permissions && !d->session ) {
        if ( !d->permissions->ready() )
            return;
        if ( !d->permissions->allowed( Permissions::Read ) ) {
            error( No, d->mailbox->name().ascii() + " is not accessible" );
            finish();
            return;
        }
        if ( !d->readOnly &&
             !d->permissions->allowed( Permissions::KeepSeen ) )
            d->readOnly = true;
    }

    if ( !::firstUnseenCache )
        ::firstUnseenCache = new SelectData::FirstUnseenCache;

    if ( mailboxGroup() && !d->sessionPreloader ) {
        d->sessionPreloader = new SessionPreloader( mailboxGroup()->contents(),
                                                    this );
        d->sessionPreloader->execute();

        IntegerSet s;
        List<Mailbox>::Iterator i( mailboxGroup()->contents() );
        while ( i ) {
            if ( !::firstUnseenCache->find( i, i->nextModSeq() ) )
                s.add( i->id() );
            ++i;
        }
        if ( s.count() > 2 ) {
            d->cacheFirstUnseen
                = new Query( "select min(uid) as uid, mailbox, "
                             "max(modseq) as modseq "
                             "from mailbox_messages mm "
                             "where mailbox=any($1) and not seen "
                             "group by mailbox", this );
            d->cacheFirstUnseen->bind( 1, s );
            d->cacheFirstUnseen->execute();
        }
    }
    if ( d->sessionPreloader ) {
        while ( d->cacheFirstUnseen && d->cacheFirstUnseen->hasResults() ) {
            Row * r = d->cacheFirstUnseen->nextRow();
            Mailbox * m = Mailbox::find( r->getInt( "mailbox" ) );
            ::firstUnseenCache->insert( m, 1 + r->getBigint( "modseq" ),
                                        r->getInt( "uid" ) );
        }
        if ( d->cacheFirstUnseen && !d->cacheFirstUnseen->done() )
            return;
        if ( !d->sessionPreloader->done() )
            return;
    }


    if ( !d->session ) {
        d->session = new ImapSession( imap(), d->mailbox, d->readOnly );
        d->session->setPermissions( d->permissions );
        imap()->beginSession( d->session );
    }

    if ( !d->session->initialised() )
        return;

    if ( d->session->isEmpty() )
        d->needFirstUnseen = false;
    else if ( ::firstUnseenCache &&
              ::firstUnseenCache->find( d->mailbox, d->session->nextModSeq() ) )
        d->needFirstUnseen = false;
    else
        d->needFirstUnseen = true;

    if ( d->needFirstUnseen && !d->firstUnseen ) {
        d->firstUnseen
            = new Query( "select uid from mailbox_messages mm "
                         "where mailbox=$1 and not seen "
                         "order by uid limit 1", this );
        d->firstUnseen->bind( 1, d->mailbox->id() );
        d->firstUnseen->execute();
    }

    if ( d->firstUnseen && !d->firstUnseen->done() )
        return;

    d->session->emitUpdates( 0 );
    // emitUpdates often calls Imap::runCommands, which calls this
    // function, which will then change its state to Finished. so
    // check that and don't repeat the last few responses.
    if ( state() != Executing )
        return;

    respond( "OK [UIDVALIDITY " + fn( d->session->uidvalidity() ) + "]"
             " uid validity" );

    if ( d->firstUnseen ) {
        if ( !::firstUnseenCache )
            ::firstUnseenCache = new SelectData::FirstUnseenCache;
        Row * r = d->firstUnseen->nextRow();
        if ( r )
            ::firstUnseenCache->insert( d->mailbox, d->session->nextModSeq(),
                                        r->getInt( "uid" ) );
    }

    if ( ::firstUnseenCache ) {
        uint unseen = ::firstUnseenCache->find( d->mailbox,
                                                d->session->nextModSeq() );
        if ( unseen )
            respond( "OK [UNSEEN " + fn( d->session->msn( unseen ) ) +
                     "] first unseen" );
    }

    if ( imap()->clientSupports( IMAP::Condstore ) &&
         !d->session->isEmpty() ) {
        uint nms = d->session->nextModSeq();
        if ( nms < 2 )
            nms = 2;
        respond( "OK [HIGHESTMODSEQ " + fn( nms-1 ) + "] highest modseq" );
    }

    if ( imap()->clientSupports( IMAP::Annotate ) ) {
        Permissions * p  = d->session->permissions();
        if ( p && p->allowed( Permissions::WriteSharedAnnotation ) )
            respond( "OK [ANNOTATIONS 262144] Arbitrary limit" );
        else
            respond( "OK [ANNOTATIONS READ-ONLY] Missing 'n' right" );
    }

    if ( d->session->readOnly() )
        setRespTextCode( "READ-ONLY" );
    else
        setRespTextCode( "READ-WRITE" );

    finish();
}
Exemple #8
0
void Status::execute()
{
    if ( state() != Executing )
        return;

    // first part. set up.
    if ( !permitted() )
        return;

    Session * session = imap()->session();
    Mailbox * current = 0;
    if ( session )
        current = session->mailbox();

    // second part. see if anything has happened, and feed the cache if
    // so. make sure we feed the cache at once.
    if ( d->unseenCount || d->recentCount || d->messageCount ) {
        if ( d->unseenCount && !d->unseenCount->done() )
            return;
        if ( d->messageCount && !d->messageCount->done() )
            return;
        if ( d->recentCount && !d->recentCount->done() )
            return;
    }
    if ( !::cache )
        ::cache = new StatusData::StatusCache;

    if ( d->unseenCount ) {
        while ( d->unseenCount->hasResults() ) {
            Row * r = d->unseenCount->nextRow();
            StatusData::CacheItem * ci =
                ::cache->find( r->getInt( "mailbox" ) );
            if ( ci ) {
                ci->hasUnseen = true;
                ci->unseen = r->getInt( "unseen" );
            }
        }
    }
    if ( d->recentCount ) {
        while ( d->recentCount->hasResults() ) {
            Row * r = d->recentCount->nextRow();
            StatusData::CacheItem * ci =
                ::cache->find( r->getInt( "mailbox" ) );
            if ( ci ) {
                ci->hasRecent = true;
                ci->recent = r->getInt( "recent" );
            }
        }
    }
    if ( d->messageCount ) {
        while ( d->messageCount->hasResults() ) {
            Row * r = d->messageCount->nextRow();
            StatusData::CacheItem * ci =
                ::cache->find( r->getInt( "mailbox" ) );
            if ( ci ) {
                ci->hasMessages = true;
                ci->messages = r->getInt( "messages" );
            }
        }
    }

    // third part. are we processing the first command in a STATUS
    // loop? if so, see if we ought to preload the cache.
    if ( mailboxGroup() && d->cacheState < 3 ) {
        IntegerSet mailboxes;
        if ( d->cacheState < 1 ) {
            // cache state 0: decide which messages
            List<Mailbox>::Iterator i( mailboxGroup()->contents() );
            while ( i ) {
                StatusData::CacheItem * ci = ::cache->provide( i );
                bool need = false;
                if ( d->unseen || d->recent || d->messages )
                    need = true;
                if ( ci->hasUnseen || ci->hasRecent || ci->hasMessages )
                    need = false;
                if ( need )
                    mailboxes.add( i->id() );
                ++i;
            }
            if ( mailboxes.count() < 3 )
                d->cacheState = 3;
        }
        if ( d->cacheState == 1 ) {
            // state 1: send queries
            if ( d->unseen ) {
                d->unseenCount
                    = new Query( "select mailbox, count(uid)::int as unseen "
                                 "from mailbox_messages "
                                 "where mailbox=any($1) and not seen "
                                 "group by mailbox", this );
                d->unseenCount->bind( 1, mailboxes );
                d->unseenCount->execute();
            }
            if ( d->recent ) {
                d->recentCount
                    = new Query( "select id as mailbox, "
                                 "uidnext-first_recent as recent "
                                 "from mailboxes where id=any($1)", this );
                d->recentCount->bind( 1, mailboxes );
                d->recentCount->execute();
            }
            if ( d->messages ) {
                d->messageCount
                    = new Query( "select count(*)::int as messages, mailbox "
                                 "from mailbox_messages where mailbox=any($1) "
                                 "group by mailbox", this );
                d->messageCount->bind( 1, mailboxes );
                d->messageCount->execute();
            }
            d->cacheState = 2;
        }
        if ( d->cacheState == 2 ) {
            // state 2: mark the cache as complete.
            IntegerSet mailboxes;
            List<Mailbox>::Iterator i( mailboxGroup()->contents() );
            while ( i ) {
                StatusData::CacheItem * ci = ::cache->find( i->id() );
                if ( ci && d->unseenCount )
                    ci->hasUnseen = true;
                if ( ci && d->recentCount )
                    ci->hasRecent = true;
                if ( ci && d->messageCount )
                    ci->hasMessages = true;
                ++i;
            }
            // and drop the queries
            d->cacheState = 3;
            d->unseenCount = 0;
            d->recentCount = 0;
            d->messageCount = 0;
        }
    }

    // the cache item we'll actually read from
    StatusData::CacheItem * i = ::cache->provide( d->mailbox );

    // fourth part: send individual queries if there's anything we need
    if ( d->unseen && !d->unseenCount && !i->hasUnseen ) {
        d->unseenCount
            = new Query( "select $1::int as mailbox, "
                         "count(uid)::int as unseen "
                         "from mailbox_messages "
                         "where mailbox=$1 and not seen", this );
        d->unseenCount->bind( 1, d->mailbox->id() );
        d->unseenCount->execute();
    }

    if ( !d->recent ) {
        // nothing doing
    }
    else if ( d->mailbox == current ) {
        // we'll pick it up from the session
    }
    else if ( i->hasRecent ) {
        // the cache has it
    }
    else if ( !d->recentCount ) {
        d->recentCount
            = new Query( "select id as mailbox, "
                         "uidnext-first_recent as recent "
                         "from mailboxes where id=$1", this );
        d->recentCount->bind( 1, d->mailbox->id() );
        d->recentCount->execute();
    }

    if ( !d->messages ) {
        // we don't need to collect
    }
    else if ( d->mailbox == current ) {
        // we'll pick it up
    }
    else if ( i->hasMessages ) {
        // the cache has it
    }
    else if ( d->messages && !d->messageCount ) {
        d->messageCount
            = new Query( "select count(*)::int as messages, "
                         "$1::int as mailbox "
                         "from mailbox_messages where mailbox=$1", this );
        d->messageCount->bind( 1, d->mailbox->id() );
        d->messageCount->execute();
    }

    if ( d->unseenCount || d->recentCount || d->messageCount ) {
        if ( d->unseenCount && !d->unseenCount->done() )
            return;
        if ( d->messageCount && !d->messageCount->done() )
            return;
        if ( d->recentCount && !d->recentCount->done() )
            return;
    }

    // fifth part: return the payload.
    EStringList status;

    if ( d->messages && i->hasMessages )
        status.append( "MESSAGES " + fn( i->messages ) );
    else if ( d->messages && d->mailbox == current )
        status.append( "MESSAGES " + fn( session->messages().count() ) );

    if ( d->recent && i->hasRecent )
        status.append( "RECENT " + fn( i->recent ) );
    else if ( d->recent && d->mailbox == current )
        status.append( "RECENT " + fn( session->recent().count() ) );

    if ( d->uidnext )
        status.append( "UIDNEXT " + fn( d->mailbox->uidnext() ) );

    if ( d->uidvalidity )
        status.append( "UIDVALIDITY " + fn( d->mailbox->uidvalidity() ) );

    if ( d->unseen && i->hasUnseen )
        status.append( "UNSEEN " + fn( i->unseen ) );

    if ( d->modseq ) {
        int64 hms = d->mailbox->nextModSeq();
        // don't like this. an empty mailbox will have a STATUS HMS of
        // 1 before it receives its first message, and also 1 after.
        if ( hms > 1 )
            hms--;
        status.append( "HIGHESTMODSEQ " + fn( hms ) );
    }

    respond( "STATUS " + imapQuoted( d->mailbox ) +
             " (" + status.join( " " ) + ")" );
    finish();
}
Exemple #9
0
void SpoolManager::execute()
{
    // Fetch a list of spooled messages, and the next time we can try
    // to deliver each of them.

    uint delay = UINT_MAX;

    if ( !d->q ) {
        IntegerSet have;
        List<DeliveryAgent>::Iterator a( d->agents );
        while ( a ) {
            if ( a->working() ) {
                have.add( a->messageId() );
                delay = 900;
                ++a;
            }
            else {
                d->agents.take( a );
            }
        }

        log( "Starting queue run" );
        d->again = false;
        reset();
        EString s( "select d.message, "
                   "extract(epoch from"
                   " min(coalesce(dr.last_attempt+interval '900 s',"
                   " d.deliver_after,"
                   " current_timestamp)))::bigint"
                   "-extract(epoch from current_timestamp)::bigint as delay "
                   "from deliveries d "
                   "join delivery_recipients dr on (d.id=dr.delivery) "
                   "where (dr.action=$1 or dr.action=$2) " );
        if ( !have.isEmpty() )
            s.append( "and not d.message=any($3) " );
        s.append( "group by d.message "
                  "order by delay" );
        d->q = new Query( s, this );
        d->q->bind( 1, Recipient::Unknown );
        d->q->bind( 2, Recipient::Delayed );
        if ( !have.isEmpty() )
            d->q->bind( 3, have );
        d->q->execute();
    }

    if ( d->q && !d->q->done() )
        return;

    // Is there anything we might do?

    if ( d->q && !d->q->rows() ) {
        // No. Just finish.
        reset();
        log( "Ending queue run" );
        return;
    }

    // Yes. What?

    if ( d->q ) {
        while ( d->q->hasResults() ) {
            Row * r = d->q->nextRow();
            int64 deliverableAt = r->getBigint( "delay" );
            if ( deliverableAt <= 0 ) {
                DeliveryAgent * a
                    = new DeliveryAgent( r->getInt( "message" ) );
                (void)new Timer( a, d->agents.count() * 5 );
                d->agents.append( a );
            }
            else if ( delay > deliverableAt )
                delay = deliverableAt;
        }
        if ( delay < UINT_MAX ) {
            log( "Will process the queue again in " +
                 fn( delay ) + " seconds" );
            d->t = new Timer( this, delay );
        }
        d->q = 0;
    }

    reset();
}
Exemple #10
0
bool PopCommand::session()
{
    if ( !d->mailbox ) {
        d->mailbox = d->pop->user()->inbox();
        log( "Attempting to start a session on " +
             d->mailbox->name().ascii() );
        d->permissions =
            new Permissions( d->mailbox, d->pop->user(), this );
    }

    if ( !d->permissions->ready() )
        return false;

    if ( !d->session ) {
        if ( !d->permissions->allowed( Permissions::Read ) ) {
            d->pop->err( "Insufficient privileges" );
            return true;
        }
        else {
            bool ro = true;
            if ( d->permissions->allowed( Permissions::KeepSeen ) &&
                 d->permissions->allowed( Permissions::DeleteMessages ) &&
                 d->permissions->allowed( Permissions::Expunge ) )
                ro = false;
            d->session =
                new PopCommandData::PopSession( d->mailbox, ro, this );
            d->session->setPermissions( d->permissions );
            d->pop->setSession( d->session );
        }
    }

    if ( !d->session->initialised() )
        return false;

    if ( !d->map ) {
        d->session->clearUnannounced();
        IntegerSet s( d->session->messages() );
        IntegerSet r;
        d->map = new Map<Message>;
        while ( !s.isEmpty() ) {
            uint uid = s.smallest();
            s.remove( uid );
            Message * m = MessageCache::provide( d->mailbox, uid );
            if ( !m->databaseId() )
                r.add( uid );
            d->map->insert( uid, m );
        }
        if ( !r.isEmpty() ) {
            d->findIds = new Query( "select message, uid "
                                    "from mailbox_messages "
                                    "where mailbox=$1 and uid=any($2)",
                                    this );
            d->findIds->bind( 1, d->mailbox->id() );
            d->findIds->bind( 2, r );
            d->findIds->execute();
        }
    }
    if ( d->findIds && !d->findIds->done() )
        return false;
    while ( d->findIds && d->findIds->hasResults() ) {
        Row * r = d->findIds->nextRow();
        Message * m = d->map->find( r->getInt( "uid" ) );
        if ( m )
            m->setDatabaseId( r->getInt( "message" ) );
    }

    d->session->clearUnannounced();
    d->pop->setMessageMap( d->map );
    d->pop->setState( POP::Transaction );
    d->pop->ok( "Done" );
    return true;
}