Files
haiku-beta6/src/kits/mail/ChainRunner.cpp
T
shatty acd73dba51 address compiler warning
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10091 a95241bf-73f2-0310-859d-f6bbb57e9c96
2004-11-21 07:22:19 +00:00

621 lines
14 KiB
C++

/* BMailChainRunner - runs the mail inbound and outbound chains
**
** Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
*/
#include <List.h>
#include <OS.h>
#include <Path.h>
#include <image.h>
#include <Entry.h>
#include <File.h>
#include <String.h>
#include <ClassInfo.h>
#include <Alert.h>
#include <Directory.h>
#include <Application.h>
#include <Locker.h>
#include <MessageQueue.h>
#include <MessageFilter.h>
#include <stdio.h>
#include <stdlib.h>
#include <MDRLanguage.h>
class _EXPORT BMailChainRunner;
#include <ChainRunner.h>
#include <status.h>
#include <StringList.h>
#include "ErrorLogWindow.h"
struct filter_image {
BMessage *settings;
BMailFilter *filter;
image_id id;
};
BLocker list_lock("mdr_chainrunner_lock");
BList running_chains, running_chain_pointers;
static void
show_error(alert_type type, const char *message, const char *tag)
{
static ErrorLogWindow *window = NULL;
static BLocker lock("error window");
lock.Lock();
if (window == NULL) {
window = new ErrorLogWindow(BRect(200, 200, 500, 250),
"Mail Daemon Status Log", B_TITLED_WINDOW);
}
lock.Unlock();
window->AddError(type, message, tag);
}
// #pragma mark -
#if USE_NASTY_SYNC_THREAD_HACK
/* There is a memory leak in gethostbyname() that causes structures it
allocates not to be freed if they were allocated from a BLooper. So
we have this awful hack to ensure that gethostbyname() is not, in
fact, called from a BLooper. It works fairly well, too. Hopefully,
the OpenBeOS net stack will rectify this problem and then I can
turn it off. That will be nice. */
int32 BMailChainRunner::thread_sync_func(void *arg) {
BMailChainRunner *us = ((BMailChainRunner *)(arg));
us->Lock();
status_t val = us->Init();
us->Unlock();
return val;
}
status_t BMailChainRunner::init_addons() {
thread_id thread = spawn_thread(&thread_sync_func,
"ChainRunnerGetHostByNameHack",10,this);
Unlock();
resume_thread(thread);
status_t result;
wait_for_thread(thread,&result);
Lock();
return result;
}
#else
#define init_addons Init
#endif
_EXPORT BMailChainRunner *
GetMailChainRunner(int32 chain_id, BMailStatusWindow *status, bool selfDestruct)
{
list_lock.Lock();
if (running_chains.HasItem((void *)(chain_id))) {
BMailChainRunner *runner = (BMailChainRunner *)running_chain_pointers.ItemAt(running_chains.IndexOf((void *)(chain_id)));
list_lock.Unlock();
return runner;
}
list_lock.Unlock();
BMailChainRunner *runner = new BMailChainRunner(GetMailChain(chain_id), status,
selfDestruct, false, selfDestruct);
runner->RunChain();
return runner;
}
class DeathFilter : public BMessageFilter {
public:
DeathFilter()
: BMessageFilter(B_QUIT_REQUESTED)
{
}
virtual filter_result Filter(BMessage *, BHandler **)
{
be_app->MessageReceived(new BMessage('enda')); //---Stop new chains from starting
list_lock.Lock();
for (int32 i = 0; i < running_chain_pointers.CountItems(); i++)
((BMailChainRunner *)(running_chain_pointers.ItemAt(i)))->Stop();
list_lock.Unlock();
while(running_chains.CountItems() > 0)
snooze(10000); // 1/100th of a second to avoid wasting CPU.
return B_DISPATCH_MESSAGE;
}
};
BMailChainRunner::BMailChainRunner(BMailChain *chain, BMailStatusWindow *status,
bool selfDestruct, bool saveChain, bool destructChain)
: BLooper(chain->Name()),
_chain(chain),
destroy_self(selfDestruct),
destroy_chain(destructChain),
save_chain(saveChain),
_status(status),
_statview(NULL),
suicide(false)
{
static DeathFilter *filter = NULL;
if (filter == NULL)
be_app->AddFilter(filter = new DeathFilter);
list_lock.Lock();
if (running_chains.HasItem((void *)(_chain->ID())))
suicide = true;
running_chains.AddItem((void *)(_chain->ID()));
running_chain_pointers.AddItem(this);
list_lock.Unlock();
}
BMailChainRunner::~BMailChainRunner()
{
//--- Delete any remaining callbacks
for (int32 i = message_cb.CountItems();i-- > 0;)
delete (BMailChainCallback *)message_cb.ItemAt(i);
for (int32 i = process_cb.CountItems();i-- > 0;)
delete (BMailChainCallback *)process_cb.ItemAt(i);
for (int32 i = chain_cb.CountItems();i-- > 0;)
delete (BMailChainCallback *)chain_cb.ItemAt(i);
//--- Delete any filter images
for (int32 i = 0; i < addons.CountItems(); i++) {
filter_image *image = (filter_image *)(addons.ItemAt(i));
delete image->filter;
delete image->settings;
unload_add_on(image->id);
delete image;
}
//--- Remove ourselves from the window if we haven't been already
if ((_status != NULL) && (_statview != NULL)) {
_status->Lock();
if (_statview->Window())
_status->RemoveView(_statview);
delete _statview;
_status->Unlock();
}
//--- Remove ourselves from the lists
list_lock.Lock();
running_chains.RemoveItem((void *)(_chain->ID()));
running_chain_pointers.RemoveItem(this);
list_lock.Unlock();
//--- And delete our chain
if (destroy_chain)
delete _chain;
}
void
BMailChainRunner::RegisterMessageCallback(BMailChainCallback *callback)
{
message_cb.AddItem(callback);
}
void
BMailChainRunner::RegisterProcessCallback(BMailChainCallback *callback)
{
process_cb.AddItem(callback);
}
void
BMailChainRunner::RegisterChainCallback(BMailChainCallback *callback)
{
chain_cb.AddItem(callback);
}
BMailChain *
BMailChainRunner::Chain()
{
return _chain;
}
status_t
BMailChainRunner::RunChain(bool asynchronous)
{
if (suicide) {
Quit();
return B_NAME_IN_USE;
}
Run();
PostMessage('INIT');
if (!asynchronous) {
status_t result;
wait_for_thread(Thread(),&result);
return result;
}
return B_OK;
}
void
BMailChainRunner::CallCallbacksFor(BList &list, status_t code)
{
for (int32 i = 0; i < list.CountItems(); i++) {
BMailChainCallback *callback = static_cast<BMailChainCallback *>(list.ItemAt(i));
callback->Callback(code);
delete callback;
}
list.MakeEmpty();
}
status_t BMailChainRunner::Init() {
status_t big_err = B_OK;
BString desc;
entry_ref addon;
MDR_DIALECT_CHOICE (
desc << ((_chain->ChainDirection() == inbound) ? "Fetching" : "Sending") << " mail for " << _chain->Name(),
desc << _chain->Name() << ((_chain->ChainDirection() == inbound) ? "より受信中..." : "へ送信中...")
);
_status->Lock();
_statview = _status->NewStatusView(desc.String(),_chain->ChainDirection() == outbound);
_status->Unlock();
BMessage settings;
for (int32 i = 0; _chain->GetFilter(i,&settings,&addon) >= B_OK; i++) {
struct filter_image *image = new struct filter_image;
BPath path(&addon);
BMailFilter *(* instantiate)(BMessage *,BMailChainRunner *);
image->id = load_add_on(path.Path());
if (image->id < B_OK) {
BString error;
MDR_DIALECT_CHOICE (
error << "Error loading the mail addon " << path.Path() << " from chain " << _chain->Name() << ": " << strerror(image->id);
ShowError(error.String());,
error << "メールアドオン " << path.Path() << " を " << _chain->Name() << "から読み込む際にエラーが発生しました: " << strerror(image->id);
ShowError(error.String());
)
return image->id;
}
status_t err = get_image_symbol(image->id,"instantiate_mailfilter",B_SYMBOL_TYPE_TEXT,(void **)&instantiate);
if (err < B_OK) {
BString error;
MDR_DIALECT_CHOICE (
error << "Error loading the mail addon " << path.Path() << " from chain " << _chain->Name()
<< ": the addon does not seem to be a mail addon (missing symbol instantiate_mailfilter).";
ShowError(error.String());,
error << "メールアドオン " << path.Path() << " を " << _chain->Name() << "から読み込む際にエラーが発生しました"
<< ": そのアドオンはメールアドオンではないようです(instantiate_mailfilterシンボルがありません)";
ShowError(error.String());
)
err = -1;
return err;
}
image->settings = new BMessage(settings);
image->settings->AddInt32("chain",_chain->ID());
image->filter = (*instantiate)(image->settings,this);
addons.AddItem(image);
if ((big_err = image->filter->InitCheck()) != B_OK) {
//printf("InitCheck() failed (%s) in add-on %s\n",strerror(big_err),path.Path());
break;
}
}
return big_err;
}
void
BMailChainRunner::MessageReceived(BMessage *msg)
{
switch (msg->what) {
case 'INIT':
if (init_addons() == B_OK)
break;
case B_QUIT_REQUESTED: {
CallCallbacksFor(chain_cb, B_OK);
// who knows what the code was?
BMessage settings;
entry_ref addon;
for (int32 i = 0; i < addons.CountItems(); i++) {
filter_image *image = (filter_image *)(addons.ItemAt(i));
delete image->filter;
if (save_chain) {
image->settings->RemoveName("chain");
_chain->GetFilter(i,&settings,&addon);
_chain->SetFilter(i,*(image->settings),addon);
}
delete image->settings;
unload_add_on(image->id);
delete image;
}
addons.MakeEmpty();
if ((_status != NULL) && (_statview != NULL)) {
_status->Lock();
if (_statview->Window())
_status->RemoveView(_statview);
else
delete _statview;
_statview = NULL;
_status->Unlock();
}
/*list_lock.Lock();
running_chains.RemoveItem((void *)(_chain->ID()));
running_chain_pointers.RemoveItem(this);
list_lock.Unlock();*/
if (save_chain)
_chain->Save();
if (destroy_self)
Quit();
break;
}
case 'GETM': {
BStringList list;
msg->FindFlat("messages",&list);
_statview->SetTotalItems(list.CountItems());
_statview->SetMaximum(msg->FindInt32("bytes"));
get_messages(&list); }
break;
case 'GTSM': {
const char *uid;
status_t err = B_OK;
msg->FindString("uid",&uid);
BEntry *entry = new BEntry(msg->FindString("into"));
_statview->SetTotalItems(1);
_statview->SetMaximum(msg->FindInt32("bytes"));
BPositionIO *file = new BFile(entry, B_READ_WRITE | B_CREATE_FILE);
BPath *folder = new BPath;
BMessage *headers = new BMessage;
headers->AddBool("ENTIRE_MESSAGE",true);
for (int32 j = 0; j < addons.CountItems(); j++) {
struct filter_image *current = (struct filter_image *)(addons.ItemAt(j));
err = current->filter->ProcessMailMessage(&file,entry,headers,folder,uid);
if (err != B_OK)
break;
}
CallCallbacksFor(message_cb, err);
delete file;
delete entry;
delete headers;
delete folder;
CallCallbacksFor(process_cb, err);
if (save_chain) {
entry_ref addon;
BMessage settings;
for (int32 i = 0; i < addons.CountItems(); i++) {
filter_image *image = (filter_image *)(addons.ItemAt(i));
BMessage *temp = new BMessage(*(image->settings));
temp->RemoveName("chain");
_chain->GetFilter(i, &settings, &addon);
_chain->SetFilter(i, *temp, addon);
delete temp;
}
_chain->Save();
}
ResetProgress();
break;
}
}
}
bool
BMailChainRunner::QuitRequested()
{
Stop();
return true;
}
void
BMailChainRunner::get_messages(BStringList *list)
{
const char *uid;
status_t err = B_OK;
const char *glort;
bool using_tmp = (_chain->MetaData()->FindString("path",&glort) < B_OK);
BDirectory tmp(using_tmp ? "/tmp" : glort);
for (int i = 0; i < list->CountItems(); i++) {
uid = (*list)[i];
char *path;
if (using_tmp)
path = tempnam("/tmp","mail_temp_");
else {
BPath pathy(glort);
pathy.Append("Downloading");
path = (char *)malloc(B_PATH_NAME_LENGTH);
sprintf(path,"%s (%s: %ld)...",pathy.Path(), _chain->Name(), _chain->ID());
}
BEntry *entry = new BEntry(path);
free(path);
BPositionIO *file = (_chain->ChainDirection() == inbound) ? new BFile(entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) : NULL;
BPath *folder = new BPath;
BMessage *headers = new BMessage;
for (int32 j = 0; j < addons.CountItems(); j++) {
struct filter_image *current = (struct filter_image *)(addons.ItemAt(j));
err = current->filter->ProcessMailMessage(&file,entry,headers,folder,uid);
if (err != B_OK)
break;
}
CallCallbacksFor(message_cb, err);
if (err == B_MAIL_DISCARD)
entry->Remove();
if (file != NULL)
delete file;
delete entry;
delete headers;
delete folder;
if (err == B_MAIL_END_FETCH || err == B_MAIL_END_CHAIN)
break;
}
CallCallbacksFor(process_cb, err);
if (save_chain) {
entry_ref addon;
BMessage settings;
for (int32 i = 0; i < addons.CountItems(); i++) {
filter_image *image = (filter_image *)(addons.ItemAt(i));
BMessage *temp = new BMessage(*(image->settings));
temp->RemoveName("chain");
_chain->GetFilter(i,&settings,&addon);
_chain->SetFilter(i,*temp,addon);
delete temp;
}
_chain->Save();
}
ResetProgress();
if (err == B_MAIL_END_CHAIN)
Stop();
}
void
BMailChainRunner::Stop(bool kill)
{
if (kill) {
BMessageQueue *looper_queue = MessageQueue();
looper_queue->Lock();
BMessage *msg;
while ((msg = looper_queue->NextMessage()))
delete msg; //-- Ensure STOP makes the front of the queue
PostMessage(B_QUIT_REQUESTED);
looper_queue->Unlock();
} else {
PostMessage(B_QUIT_REQUESTED);
}
}
void
BMailChainRunner::GetMessages(BStringList *list, int32 bytes)
{
if (list->CountItems() < 1)
return;
BMessage msg('GETM');
msg.AddFlat("messages",list);
msg.AddInt32("bytes",bytes);
PostMessage(&msg);
}
void
BMailChainRunner::GetSingleMessage(const char *uid, int32 length, BPath *into)
{
BMessage msg('GTSM');
msg.AddString("uid",uid);
msg.AddInt32("bytes",length);
msg.AddString("into",into->Path());
PostMessage(&msg);
}
void
BMailChainRunner::ReportProgress(int bytes, int messages, const char *message)
{
if (bytes != 0)
_statview->AddProgress(bytes);
for (int i = 0; i < messages; i++)
_statview->AddItem();
if (message != NULL)
_statview->SetMessage(message);
}
void
BMailChainRunner::ResetProgress(const char *message)
{
_statview->Reset();
if (message != NULL)
_statview->SetMessage(message);
}
void
BMailChainRunner::ShowError(const char *error)
{
BString tag = Chain()->Name();
tag << ": ";
show_error(B_WARNING_ALERT, error, tag.String());
}
void
BMailChainRunner::ShowMessage(const char *error)
{
BString tag = Chain()->Name();
tag << ": ";
show_error(B_INFO_ALERT, error, tag.String());
}