2004-09-20 22:31:50 +00:00
#include <NodeMonitor.h>
#include <Handler.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#include <String.h>
#include <File.h>
#include <map>
2004-10-16 17:21:26 +00:00
class _EXPORT BRemoteMailStorageProtocol ;
2004-09-20 22:31:50 +00:00
#include <RemoteStorageProtocol.h>
#include <ChainRunner.h>
2004-10-30 19:34:01 +00:00
#include <E-mail.h>
2004-09-20 22:31:50 +00:00
namespace {
void GetSubFolders ( BDirectory * of , BStringList * folders , const char * prepend = "" );
class UpdateHandler : public BHandler {
public :
2004-10-16 17:21:26 +00:00
UpdateHandler ( BRemoteMailStorageProtocol * prot , const char * dest ) : _prot ( prot ), _dest ( dest ) {
2004-09-20 22:31:50 +00:00
node_ref ref ;
_dest . GetNodeRef ( & ref );
dest_node = ref . node ;
}
virtual ~ UpdateHandler () {
stop_watching ( this );
}
void MessageReceived ( BMessage * msg ) {
switch ( msg -> what ) {
case ' INIT ' : {
if ( _prot -> InitCheck () < B_OK )
return ;
(( BMailChainRunner * )( Looper ())) -> ReportProgress ( 0 , 0 , "Synchronizing Mailboxes" );
BStringList subdirs ;
GetSubFolders ( & _dest , & subdirs );
BStringList to_delete ;
BStringList to_add ;
subdirs . NotThere ( _prot -> mailboxes , & to_add );
if ( subdirs . CountItems () != 0 ) // --- If it's a virgin mailfolder, the user probably just configured his machineand probably *doesn't* want all his mail folders deleted :)
subdirs . NotHere ( _prot -> mailboxes , & to_delete );
for ( int32 i = 0 ; i < to_add . CountItems (); i ++ ) {
if ( _prot -> CreateMailbox ( to_add [ i ]) != B_OK )
continue ;
_prot -> mailboxes += to_add [ i ];
_prot -> SyncMailbox ( to_add [ i ]);
}
_prot -> CheckForDeletedMessages (); //--- Refresh the manifest list, delete messages in locally deleted folders
for ( int32 i = 0 ; i < to_delete . CountItems (); i ++ ) {
if ( to_delete [ i ][ 0 ] == 0 )
continue ;
if ( _prot -> DeleteMailbox ( to_delete [ i ]) == B_OK )
_prot -> mailboxes -= to_delete [ i ];
}
entry_ref ref ;
BEntry entry ;
_dest . GetEntry ( & entry );
entry . GetRef ( & ref );
BPath path ( & ref ), work_path ( path );
BNode node ( & ref );
node_ref watcher ;
node . GetNodeRef ( & watcher );
watch_node ( & watcher , B_WATCH_DIRECTORY , this );
for ( int32 i = 0 ; i < _prot -> mailboxes . CountItems (); i ++ ) {
work_path = path ;
work_path . Append ( _prot -> mailboxes [ i ]);
node . SetTo ( work_path . Path ());
node . GetNodeRef ( & watcher );
nodes [ watcher . node ] = strdup ( _prot -> mailboxes [ i ]);
if ( _prot -> mailboxes [ i ][ 0 ] == 0 )
continue ; //--- We've covered this in the parent monitor
watch_node ( & watcher , B_WATCH_DIRECTORY , this );
_prot -> SyncMailbox ( _prot -> mailboxes [ i ]);
}
(( BMailChainRunner * )( Looper ())) -> ResetProgress ();
} break ;
case B_NODE_MONITOR : {
int32 opcode ;
if ( msg -> FindInt32 ( "opcode" , & opcode ) < B_OK )
break ;
int64 directory , node ( msg -> FindInt64 ( "node" ));
dev_t device ( msg -> FindInt32 ( "device" ));
bool is_dir ;
{
node_ref item_ref ;
item_ref . node = node ;
item_ref . device = device ;
BDirectory dir ( & item_ref );
is_dir = ( dir . InitCheck () == B_OK );
}
if ( opcode == B_ENTRY_MOVED ) {
ino_t from , to ;
msg -> FindInt64 ( "from directory" , & from );
msg -> FindInt64 ( "to directory" , & to );
const char * from_mb ( nodes [ from ]), * to_mb ( nodes [ to ]);
if ( to == dest_node )
to_mb = "" ;
if ( from == dest_node )
from_mb = "" ;
if ( from_mb == NULL ) {
msg -> AddInt64 ( "directory" , to );
opcode = B_ENTRY_CREATED ;
} else if ( to_mb == NULL ) {
msg -> AddInt64 ( "directory" , from );
if ( is_dir )
opcode = B_ENTRY_REMOVED ;
} else {
if ( ! is_dir ) {
{
node_ref item_ref ;
item_ref . node = to ;
item_ref . device = device ;
BDirectory dir ( & item_ref );
BNode node ( & dir , msg -> FindString ( "name" ));
//--- Why in HELL can't you make a BNode from a node_ref?
if ( node . InitCheck () != B_OK )
break ; //-- We're late, it's already gone elsewhere. Ignore for now.
BString id ;
node . ReadAttrString ( "MAIL:unique_id" , & id );
id . Truncate ( id . FindLast ( '/' ));
if ( id == to_mb )
break ; //-- Already where it belongs, no need to do anything
}
snooze ( uint64 ( 5e5 ));
_prot -> SyncMailbox ( to_mb );
//node.WriteAttrString("MAIL:unique_id",&id);
_prot -> CheckForDeletedMessages ();
} else {
BString mb ;
if ( to_mb [ 0 ] == 0 )
mb = msg -> FindString ( "name" );
else {
mb = to_mb ;
mb << '/' << msg -> FindString ( "name" );
}
if ( strcmp ( mb . String (), nodes [ node ]) == 0 )
break ;
if ( _prot -> CreateMailbox ( mb . String ()) < B_OK )
break ;
_prot -> mailboxes += mb . String ();
_prot -> SyncMailbox ( mb . String ());
_prot -> CheckForDeletedMessages ();
_prot -> DeleteMailbox ( nodes [ node ]);
_prot -> mailboxes -= nodes [ node ];
free (( void * ) nodes [ node ]);
nodes [ node ] = strdup ( mb . String ());
}
break ;
}
}
msg -> FindInt64 ( "directory" , & directory );
switch ( opcode ) {
case B_ENTRY_CREATED :
if ( ! is_dir ) {
const char * dir = nodes [ directory ];
snooze ( 500000 );
if ( dir == NULL )
dir = "" ;
2004-11-07 21:50:46 +00:00
if ( ! _prot -> mailboxes . HasItem ( dir ))
break ;
2004-09-20 22:31:50 +00:00
{
node_ref item_ref ;
item_ref . node = directory ;
item_ref . device = device ;
BDirectory dir ( & item_ref );
BNode node ( & dir , msg -> FindString ( "name" ));
//--- Why in HELL can't you make a BNode from a node_ref?
if ( node . InitCheck () != B_OK )
break ; //-- We're late, it's already gone elsewhere. Ignore for now.
}
_prot -> SyncMailbox ( nodes [ directory ]);
} else {
BString mb ;
if ( directory == dest_node )
mb = msg -> FindString ( "name" );
else {
mb = nodes [ directory ];
mb << '/' << msg -> FindString ( "name" );
}
if ( _prot -> CreateMailbox ( mb . String ()) < B_OK )
break ;
nodes [ node ] = strdup ( mb . String ());
_prot -> mailboxes += mb . String ();
_prot -> SyncMailbox ( mb . String ());
node_ref ref ;
ref . device = device ;
ref . node = node ;
watch_node ( & ref , B_WATCH_DIRECTORY , this );
}
break ;
case B_ENTRY_REMOVED :
_prot -> CheckForDeletedMessages ();
if (( is_dir ) && ( nodes [ node ] != NULL )) {
_prot -> DeleteMailbox ( nodes [ node ]);
_prot -> mailboxes -= nodes [ node ];
free (( void * ) nodes [ node ]);
nodes [ node ] = NULL ;
node_ref ref ;
ref . device = device ;
ref . node = node ;
watch_node ( & ref , B_STOP_WATCHING , this );
}
break ;
}
} break ;
}
}
private :
2004-10-16 17:21:26 +00:00
BRemoteMailStorageProtocol * _prot ;
2004-09-20 22:31:50 +00:00
BDirectory _dest ;
map < int64 , const char *> nodes ;
ino_t dest_node ;
};
void GetSubFolders ( BDirectory * of , BStringList * folders , const char * prepend ) {
of -> Rewind ();
BEntry ent ;
BString crud ;
BDirectory sub ;
char buf [ 255 ];
while ( of -> GetNextEntry ( & ent ) == B_OK ) {
if ( ent . IsDirectory ()) {
sub . SetTo ( & ent );
ent . GetName ( buf );
crud = prepend ;
crud << buf << '/' ;
GetSubFolders ( & sub , folders , crud . String ());
crud = prepend ;
crud << buf ;
( * folders ) += crud . String ();
}
}
}
2004-10-16 17:21:26 +00:00
BRemoteMailStorageProtocol :: BRemoteMailStorageProtocol ( BMessage * settings , BMailChainRunner * runner ) : BMailProtocol ( settings , runner ) {
2004-09-20 22:31:50 +00:00
handler = new UpdateHandler ( this , runner -> Chain () -> MetaData () -> FindString ( "path" ));
runner -> AddHandler ( handler );
runner -> PostMessage ( ' INIT ' , handler );
}
2004-10-16 17:21:26 +00:00
BRemoteMailStorageProtocol ::~ BRemoteMailStorageProtocol () {
2004-09-20 22:31:50 +00:00
delete handler ;
}
}
//----BMailProtocol stuff
2004-10-16 17:21:26 +00:00
status_t BRemoteMailStorageProtocol :: GetMessage (
2004-09-20 22:31:50 +00:00
const char * uid ,
BPositionIO ** out_file , BMessage * out_headers ,
BPath * out_folder_location ) {
BString folder ( uid ), id ;
{
BString raw ( uid );
folder . Truncate ( raw . FindLast ( '/' ));
raw . CopyInto ( id , raw . FindLast ( '/' ) + 1 , raw . Length ());
}
* out_folder_location = folder . String ();
return GetMessage ( folder . String (), id . String (), out_file , out_headers );
}
2004-10-16 17:21:26 +00:00
status_t BRemoteMailStorageProtocol :: DeleteMessage ( const char * uid ) {
2004-09-20 22:31:50 +00:00
BString folder ( uid ), id ;
{
BString raw ( uid );
int32 j = raw . FindLast ( '/' );
folder . Truncate ( j );
raw . CopyInto ( id , j + 1 , raw . Length ());
}
status_t err ;
if (( err = DeleteMessage ( folder . String (), id . String ())) < B_OK )
return err ;
( * unique_ids ) -= uid ;
return B_OK ;
}
2004-10-16 17:21:26 +00:00
void BRemoteMailStorageProtocol :: SyncMailbox ( const char * mailbox ) {
2004-09-20 22:31:50 +00:00
BPath path ( runner -> Chain () -> MetaData () -> FindString ( "path" ));
path . Append ( mailbox );
BDirectory folder ( path . Path ());
BEntry entry ;
BFile snoodle ;
BString string ;
uint32 chain ;
bool append ;
2004-11-07 21:50:46 +00:00
if ( ! mailboxes . HasItem ( mailbox ))
return ; // -- Basic Sanity Checking
2004-09-20 22:31:50 +00:00
while ( folder . GetNextEntry ( & entry ) == B_OK ) {
if ( ! entry . IsFile ())
continue ;
while ( snoodle . SetTo ( & entry , B_READ_WRITE ) == B_BUSY ) snooze ( 100 );
append = false ;
while ( snoodle . Lock () != B_OK ) snooze ( 100 );
snoodle . Unlock ();
if ( snoodle . ReadAttr ( "MAIL:chain" , B_INT32_TYPE , 0 , & chain , sizeof ( chain )) < B_OK )
append = true ;
2004-10-30 19:34:01 +00:00
if ( chain != runner -> Chain () -> ID ()) {
int32 pending_chain ( - 1 ), flags ( 0 );
snoodle . ReadAttr ( "MAIL:pending_chain" , B_INT32_TYPE , 0 , & chain , sizeof ( chain ));
snoodle . ReadAttr ( "MAIL:flags" , B_INT32_TYPE , 0 , & flags , sizeof ( flags ));
if (( pending_chain == runner -> Chain () -> ID ()) && ( BMailChain ( chain ). ChainDirection () == outbound ) && ( flags & B_MAIL_PENDING ))
continue ; //--- Ignore this message, recode the chain attribute at the next SyncMailbox()
if ( pending_chain == runner -> Chain () -> ID ()) {
chain = runner -> Chain () -> ID ();
snoodle . WriteAttr ( "MAIL:chain" , B_INT32_TYPE , 0 , & chain , sizeof ( chain ));
append = false ;
} else
append = true ;
}
2004-09-20 22:31:50 +00:00
if ( snoodle . ReadAttrString ( "MAIL:unique_id" , & string ) < B_OK )
append = true ;
BString folder ( string ), id ( "" );
int32 j = string . FindLast ( '/' );
if (( ! append ) && ( j >= 0 )) {
folder . Truncate ( j );
string . CopyInto ( id , j + 1 , string . Length ());
if ( folder == mailbox )
continue ;
} else {
append = true ;
}
if ( append )
AddMessage ( mailbox , & snoodle , & id ); //---We should check for partial messages here
else
CopyMessage ( folder . String (), mailbox , & id );
string = mailbox ;
string << '/' << id ;
/*snoodle.RemoveAttr("MAIL:unique_id");
snoodle.RemoveAttr("MAIL:chain");*/
chain = runner -> Chain () -> ID ();
2004-10-30 19:34:01 +00:00
int32 flags = 0 ;
snoodle . ReadAttr ( "MAIL:flags" , B_INT32_TYPE , 0 , & flags , sizeof ( flags ));
if ( flags & B_MAIL_PENDING )
snoodle . WriteAttr ( "MAIL:pending_chain" , B_INT32_TYPE , 0 , & chain , sizeof ( chain ));
else
snoodle . WriteAttr ( "MAIL:chain" , B_INT32_TYPE , 0 , & chain , sizeof ( chain ));
2004-09-20 22:31:50 +00:00
snoodle . WriteAttrString ( "MAIL:unique_id" , & string );
( * manifest ) += string . String ();
( * unique_ids ) += string . String ();
string = runner -> Chain () -> Name ();
snoodle . WriteAttrString ( "MAIL:account" , & string );
}
}
2004-10-16 17:21:26 +00:00
/*status_t BRemoteMailStorageProtocol::MoveMessage(const char *mailbox, const char *to_mailbox, BString *message) {
2004-09-20 22:31:50 +00:00
BString new_id(*message);
status_t err;
if ((err = CopyMessage(mailbox,to_mailbox,&new_id)) < B_OK)
return err;
if ((err = DeleteMessage(mailbox,message->String())) < B_OK)
return err;
*message = new_id;
return B_OK;
}*/