#include "TRoster.h"
#include <new>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <Application.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#include <AppMisc.h>
#include <MessagePrivate.h>
#include <MessengerPrivate.h>
#include <RosterPrivate.h>
#include <ServerProtocol.h>
#include <storage_support.h>
#include "AppInfoListMessagingTargetSet.h"
#include "Debug.h"
#include "EventMaskWatcher.h"
#include "MessageDeliverer.h"
#include "RegistrarDefs.h"
#include "RosterAppInfo.h"
#include "RosterSettingsCharStream.h"
using std::nothrow;
using namespace BPrivate;
const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
static const char*
get_default_roster_settings_path(BPath& path, bool createDirectory)
{
status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
if (error == B_OK)
error = path.Append("system/registrar");
if (error == B_OK && createDirectory)
error = create_directory(path.Path(), 0777);
if (error == B_OK)
error = path.Append("RosterSettings");
return path.Path();
}
bool
larger_index(const recent_entry* entry1, const recent_entry* entry2)
{
if (entry1 && entry2)
return entry1->index > entry2->index;
return true;
}
TRoster::TRoster()
:
fLock("roster"),
fRegisteredApps(),
fEarlyPreRegisteredApps(),
fIARRequestsByID(),
fIARRequestsByToken(),
fActiveApp(NULL),
fWatchingService(),
fRecentApps(),
fRecentDocuments(),
fRecentFolders(),
fLastToken(0),
fShuttingDown(false)
{
find_directory(B_SYSTEM_DIRECTORY, &fSystemAppPath);
find_directory(B_SYSTEM_SERVERS_DIRECTORY, &fSystemServerPath);
}
TRoster::~TRoster()
{
}
void
TRoster::HandleAddApplication(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
const char* signature;
entry_ref ref;
uint32 flags;
team_id team;
thread_id thread;
port_id port;
bool fullReg;
if (request->FindString("signature", &signature) != B_OK)
signature = NULL;
if (request->FindRef("ref", &ref) != B_OK)
SET_ERROR(error, B_BAD_VALUE);
if (request->FindInt32("flags", (int32*)&flags) != B_OK)
flags = B_REG_DEFAULT_APP_FLAGS;
if (request->FindInt32("team", &team) != B_OK)
team = -1;
if (request->FindInt32("thread", &thread) != B_OK)
thread = -1;
if (request->FindInt32("port", &port) != B_OK)
port = -1;
if (request->FindBool("full_registration", &fullReg) != B_OK)
fullReg = false;
PRINT("team: %" B_PRId32 ", signature: %s\n", team, signature);
PRINT("full registration: %d\n", fullReg);
if (fShuttingDown)
error = B_SHUTTING_DOWN;
team_id otherTeam = -1;
uint32 token = 0;
uint32 launchFlags = flags & B_LAUNCH_MASK;
BEntry entry(&ref);
if (!entry.Exists())
SET_ERROR(error, B_ENTRY_NOT_FOUND);
if (error == B_OK)
_ValidateRunning(ref, signature);
if (error == B_OK) {
PRINT("flags: %" B_PRIx32 "\n", flags);
PRINT("ref: %" B_PRId32 ", %" B_PRId64 ", %s\n", ref.device,
ref.directory, ref.name);
RosterAppInfo* info = NULL;
if ((launchFlags == B_SINGLE_LAUNCH
|| launchFlags == B_EXCLUSIVE_LAUNCH)
&& ((info = fRegisteredApps.InfoFor(&ref)) != NULL
|| (info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL)) {
SET_ERROR(error, B_ALREADY_RUNNING);
otherTeam = info->team;
token = info->token;
}
}
if (error == B_OK && signature) {
RosterAppInfo* info = NULL;
if (launchFlags == B_EXCLUSIVE_LAUNCH
&& (((info = fRegisteredApps.InfoFor(signature)))
|| ((info = fEarlyPreRegisteredApps.InfoFor(signature))))) {
SET_ERROR(error, B_ALREADY_RUNNING);
otherTeam = info->team;
token = info->token;
}
}
if (error == B_OK) {
if (team < 0) {
if (fullReg)
SET_ERROR(error, B_BAD_VALUE);
} else if (fRegisteredApps.InfoFor(team))
SET_ERROR(error, B_REG_ALREADY_REGISTERED);
}
if (error == B_OK) {
RosterAppInfo* info = new(nothrow) RosterAppInfo;
if (info) {
info->Init(thread, team, port, flags, &ref, signature);
if (fullReg)
info->state = APP_STATE_REGISTERED;
else
info->state = APP_STATE_PRE_REGISTERED;
info->registration_time = system_time();
bool addingSuccess = false;
if (team >= 0) {
PRINT("added ref: %" B_PRId32 ", %" B_PRId64 ", %s\n",
info->ref.device, info->ref.directory, info->ref.name);
addingSuccess = (AddApp(info) == B_OK);
if (addingSuccess && fullReg)
_AppAdded(info);
} else {
token = info->token = _NextToken();
addingSuccess = fEarlyPreRegisteredApps.AddInfo(info);
PRINT("added to early pre-regs, token: %" B_PRIu32 "\n", token);
}
if (!addingSuccess)
SET_ERROR(error, B_NO_MEMORY);
} else
SET_ERROR(error, B_NO_MEMORY);
if (error != B_OK && info)
delete info;
}
if (error == B_OK) {
if (signature && signature[0] != '\0')
fRecentApps.Add(signature, flags);
else
fRecentApps.Add(&ref, flags);
BMessage reply(B_REG_SUCCESS);
if (team < 0)
reply.AddInt32("token", (int32)token);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
if (otherTeam >= 0)
reply.AddInt32("other_team", otherTeam);
if (token > 0)
reply.AddInt32("token", (int32)token);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleCompleteRegistration(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
thread_id thread;
port_id port;
if (request->FindInt32("team", &team) != B_OK)
team = -1;
if (request->FindInt32("thread", &thread) != B_OK)
thread = -1;
if (request->FindInt32("port", &port) != B_OK)
port = -1;
if (fShuttingDown)
error = B_SHUTTING_DOWN;
if (error == B_OK && port < 0)
SET_ERROR(error, B_BAD_VALUE);
if (error == B_OK && thread < 0)
SET_ERROR(error, B_BAD_VALUE);
if (error == B_OK) {
if (team >= 0) {
RosterAppInfo* info = fRegisteredApps.InfoFor(team);
if (info && info->state == APP_STATE_PRE_REGISTERED) {
info->thread = thread;
info->port = port;
info->state = APP_STATE_REGISTERED;
_AppAdded(info);
} else
SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
} else
SET_ERROR(error, B_BAD_VALUE);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleIsAppRegistered(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
entry_ref ref;
team_id team;
uint32 token;
if (request->FindRef("ref", &ref) != B_OK)
SET_ERROR(error, B_BAD_VALUE);
if (request->FindInt32("team", &team) != B_OK)
team = -1;
if (request->FindInt32("token", (int32*)&token) != B_OK)
token = 0;
PRINT("team: %" B_PRId32 ", token: %" B_PRIu32 "\n", team, token);
PRINT("ref: %" B_PRId32 ", %" B_PRId64 ", %s\n", ref.device, ref.directory,
ref.name);
if (error == B_OK && !BEntry(&ref).Exists())
SET_ERROR(error, B_ENTRY_NOT_FOUND);
if (error == B_OK && team < 0 && token == 0)
SET_ERROR(error, B_BAD_VALUE);
RosterAppInfo* info = NULL;
if (error == B_OK) {
if ((info = fRegisteredApps.InfoFor(team)) != NULL) {
PRINT("found team in fRegisteredApps\n");
_ReplyToIARRequest(request, info);
} else if (token > 0
&& (info = fEarlyPreRegisteredApps.InfoForToken(token)) != NULL) {
PRINT("found ref in fEarlyRegisteredApps (by token)\n");
be_app->DetachCurrentMessage();
_AddIARRequest(fIARRequestsByToken, token, request);
} else if (team >= 0
&& (info = fEarlyPreRegisteredApps.InfoFor(&ref)) != NULL) {
PRINT("found ref in fEarlyRegisteredApps (by ref)\n");
be_app->DetachCurrentMessage();
_AddIARRequest(fIARRequestsByID, team, request);
} else {
PRINT("didn't find team or ref\n");
_ReplyToIARRequest(request, NULL);
}
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleRemovePreRegApp(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
uint32 token;
if (request->FindInt32("token", (int32*)&token) != B_OK)
SET_ERROR(error, B_BAD_VALUE);
if (error == B_OK) {
RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
if (info) {
fEarlyPreRegisteredApps.RemoveInfo(info);
delete info;
} else
SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleRemoveApp(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
error = request->FindInt32("team", &team);
PRINT("team: %" B_PRId32 "\n", team);
if (error == B_OK) {
if (RosterAppInfo* info = fRegisteredApps.InfoFor(team)) {
RemoveApp(info);
delete info;
} else
SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleSetThreadAndTeam(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
thread_id thread;
uint32 token;
if (request->FindInt32("team", &team) != B_OK)
team = -1;
if (request->FindInt32("thread", &thread) != B_OK)
thread = -1;
if (request->FindInt32("token", (int32*)&token) != B_OK)
SET_ERROR(error, B_BAD_VALUE);
if (error == B_OK && team < 0)
SET_ERROR(error, B_BAD_VALUE);
PRINT("team: %" B_PRId32 ", thread: %" B_PRId32 ", token: %" B_PRIu32 "\n",
team, thread, token);
port_id port = -1;
if (error == B_OK) {
RosterAppInfo* info = fEarlyPreRegisteredApps.InfoForToken(token);
if (info != NULL) {
fEarlyPreRegisteredApps.RemoveInfo(info);
info->team = team;
info->thread = thread;
info->port = port = create_port(B_REG_APP_LOOPER_PORT_CAPACITY,
kRAppLooperPortName);
if (info->port < 0)
SET_ERROR(error, info->port);
if (error == B_OK)
SET_ERROR(error, set_port_owner(info->port, team));
if (error == B_OK)
SET_ERROR(error, AddApp(info));
if (error != B_OK) {
if (info->port >= 0)
delete_port(info->port);
delete info;
info = NULL;
}
IARRequestMap::iterator it = fIARRequestsByID.find(team);
if (it != fIARRequestsByID.end()) {
BMessageQueue* requests = it->second;
if (error == B_OK)
_ReplyToIARRequests(requests, info);
delete requests;
fIARRequestsByID.erase(it);
}
it = fIARRequestsByToken.find((int32)token);
if (it != fIARRequestsByToken.end()) {
BMessageQueue* requests = it->second;
if (error == B_OK)
_ReplyToIARRequests(requests, info);
delete requests;
fIARRequestsByToken.erase(it);
}
} else
SET_ERROR(error, B_REG_APP_NOT_PRE_REGISTERED);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
reply.AddInt32("port", port);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleSetSignature(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
const char* signature;
if (request->FindInt32("team", &team) != B_OK)
error = B_BAD_VALUE;
if (request->FindString("signature", &signature) != B_OK)
error = B_BAD_VALUE;
if (error == B_OK) {
if (RosterAppInfo* info = fRegisteredApps.InfoFor(team))
strcpy(info->signature, signature);
else
SET_ERROR(error, B_REG_APP_NOT_REGISTERED);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleGetAppInfo(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
team_id team;
entry_ref ref;
const char* signature;
bool hasTeam = true;
bool hasRef = true;
bool hasSignature = true;
if (request->FindInt32("team", &team) != B_OK)
hasTeam = false;
if (request->FindRef("ref", &ref) != B_OK)
hasRef = false;
if (request->FindString("signature", &signature) != B_OK)
hasSignature = false;
if (hasTeam)
PRINT("team: %" B_PRId32 "\n", team);
if (hasRef) {
PRINT("ref: %" B_PRId32 ", %" B_PRId64 ", %s\n", ref.device,
ref.directory, ref.name);
}
if (hasSignature)
PRINT("signature: %s\n", signature);
RosterAppInfo* info = NULL;
status_t error = B_OK;
if (hasTeam) {
info = fRegisteredApps.InfoFor(team);
if (info == NULL)
SET_ERROR(error, B_BAD_TEAM_ID);
} else if (hasRef) {
info = fRegisteredApps.InfoFor(&ref);
if (info == NULL)
SET_ERROR(error, B_ERROR);
} else if (hasSignature) {
info = fRegisteredApps.InfoFor(signature);
if (info == NULL)
SET_ERROR(error, B_ERROR);
} else {
if (fActiveApp)
info = fActiveApp;
else
SET_ERROR(error, B_ERROR);
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
_AddMessageAppInfo(&reply, info);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleGetAppList(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
const char* signature;
if (request->FindString("signature", &signature) != B_OK)
signature = NULL;
BMessage reply(B_REG_SUCCESS);
for (AppInfoList::Iterator it(fRegisteredApps.It());
RosterAppInfo* info = *it;
++it) {
if (info->state != APP_STATE_REGISTERED)
continue;
if (signature == NULL || strcasecmp(signature, info->signature) == 0)
reply.AddInt32("teams", info->team);
}
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleUpdateActiveApp(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
if (request->FindInt32("team", &team) != B_OK)
error = B_BAD_VALUE;
if (error == B_OK) {
if (RosterAppInfo* info = fRegisteredApps.InfoFor(team))
UpdateActiveApp(info);
else
error = B_BAD_TEAM_ID;
}
if (request->IsSourceWaiting()) {
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
}
FUNCTION_END();
}
void
TRoster::HandleBroadcast(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
team_id team;
BMessage message;
BMessenger replyTarget;
if (request->FindInt32("team", &team) != B_OK)
team = -1;
if (error == B_OK && request->FindMessage("message", &message) != B_OK)
error = B_BAD_VALUE;
if (error == B_OK
&& request->FindMessenger("reply_target", &replyTarget) != B_OK) {
error = B_BAD_VALUE;
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
if (error == B_OK) {
class BroadcastMessagingTargetSet
: public AppInfoListMessagingTargetSet {
public:
BroadcastMessagingTargetSet(AppInfoList& list, team_id team)
: AppInfoListMessagingTargetSet(list, true),
fTeam(team)
{
}
virtual bool Filter(const RosterAppInfo* info)
{
return AppInfoListMessagingTargetSet::Filter(info)
&& (info->team != fTeam);
}
private:
team_id fTeam;
} targetSet(fRegisteredApps, team);
if (targetSet.HasNext()) {
BMessage::Private(message).SetReply(replyTarget);
MessageDeliverer::Default()->DeliverMessage(&message, targetSet);
}
}
FUNCTION_END();
}
void
TRoster::HandleStartWatching(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
BMessenger target;
uint32 events;
if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
error = B_BAD_VALUE;
if (request->FindInt32("events", (int32*)&events) != B_OK)
error = B_BAD_VALUE;
if (error == B_OK) {
Watcher* watcher = new(nothrow) EventMaskWatcher(target, events);
if (watcher) {
if (!fWatchingService.AddWatcher(watcher)) {
error = B_NO_MEMORY;
delete watcher;
}
} else
error = B_NO_MEMORY;
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleStopWatching(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
status_t error = B_OK;
BMessenger target;
if (error == B_OK && request->FindMessenger("target", &target) != B_OK)
error = B_BAD_VALUE;
if (error == B_OK) {
if (!fWatchingService.RemoveWatcher(target))
error = B_BAD_VALUE;
}
if (error == B_OK) {
BMessage reply(B_REG_SUCCESS);
request->SendReply(&reply);
} else {
BMessage reply(B_REG_ERROR);
reply.AddInt32("error", error);
request->SendReply(&reply);
}
FUNCTION_END();
}
void
TRoster::HandleGetRecentDocuments(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
_HandleGetRecentEntries(request);
FUNCTION_END();
}
void
TRoster::HandleGetRecentFolders(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
_HandleGetRecentEntries(request);
FUNCTION_END();
}
void
TRoster::HandleGetRecentApps(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleGetRecentApps(NULL) called\n"));
return;
}
int32 maxCount;
BMessage reply(B_REG_RESULT);
status_t error = request->FindInt32("max count", &maxCount);
if (!error)
error = fRecentApps.Get(maxCount, &reply);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleAddToRecentDocuments(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n"));
return;
}
entry_ref ref;
const char* appSig;
BMessage reply(B_REG_RESULT);
status_t error = request->FindRef("ref", &ref);
if (!error)
error = request->FindString("app sig", &appSig);
if (!error)
error = fRecentDocuments.Add(&ref, appSig);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleAddToRecentFolders(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n"));
return;
}
entry_ref ref;
const char* appSig;
BMessage reply(B_REG_RESULT);
status_t error = request->FindRef("ref", &ref);
if (!error)
error = request->FindString("app sig", &appSig);
if (!error)
error = fRecentFolders.Add(&ref, appSig);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleAddToRecentApps(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n"));
return;
}
const char* appSig;
BMessage reply(B_REG_RESULT);
status_t error = request->FindString("app sig", &appSig);
if (!error)
error = fRecentApps.Add(appSig);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleLoadRecentLists(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n"));
return;
}
const char* filename;
BMessage reply(B_REG_RESULT);
status_t error = request->FindString("filename", &filename);
if (!error)
error = _LoadRosterSettings(filename);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleSaveRecentLists(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
if (!request) {
D(PRINT("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n"));
return;
}
const char* filename;
BMessage reply(B_REG_RESULT);
status_t error = request->FindString("filename", &filename);
if (!error)
error = _SaveRosterSettings(filename);
reply.AddInt32("result", error);
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::HandleAppServerStarted(BMessage* request)
{
BAutolock _(fLock);
AppInfoListMessagingTargetSet targetSet(fRegisteredApps);
if (targetSet.HasNext()) {
BMessage message(kMsgAppServerStarted);
MessageDeliverer::Default()->DeliverMessage(&message, targetSet);
}
}
void
TRoster::ClearRecentDocuments()
{
BAutolock _(fLock);
fRecentDocuments.Clear();
}
void
TRoster::ClearRecentFolders()
{
BAutolock _(fLock);
fRecentFolders.Clear();
}
void
TRoster::ClearRecentApps()
{
BAutolock _(fLock);
fRecentApps.Clear();
}
status_t
TRoster::Init()
{
if (fLock.InitCheck() < 0)
return fLock.InitCheck();
RosterAppInfo* info = new(nothrow) RosterAppInfo;
if (info == NULL)
return B_NO_MEMORY;
entry_ref ref;
status_t error = get_app_ref(&ref);
if (error == B_OK) {
info->Init(be_app->Thread(), be_app->Team(),
BMessenger::Private(be_app_messenger).Port(),
B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP, &ref, B_REGISTRAR_SIGNATURE);
info->state = APP_STATE_REGISTERED;
info->registration_time = system_time();
error = AddApp(info);
}
if (error == B_OK)
_LoadRosterSettings();
if (error != B_OK)
delete info;
return error;
}
status_t
TRoster::AddApp(RosterAppInfo* info)
{
BAutolock _(fLock);
status_t error = (info ? B_OK : B_BAD_VALUE);
if (info) {
if (!fRegisteredApps.AddInfo(info))
error = B_NO_MEMORY;
}
return error;
}
void
TRoster::RemoveApp(RosterAppInfo* info)
{
BAutolock _(fLock);
if (info) {
if (fRegisteredApps.RemoveInfo(info)) {
if (info->state == APP_STATE_REGISTERED) {
info->state = APP_STATE_UNREGISTERED;
_AppRemoved(info);
}
}
}
}
void
TRoster::UpdateActiveApp(RosterAppInfo* info)
{
BAutolock _(fLock);
if (info != fActiveApp) {
RosterAppInfo* oldActiveApp = fActiveApp;
fActiveApp = NULL;
if (oldActiveApp)
_AppDeactivated(oldActiveApp);
if (info) {
fActiveApp = info;
_AppActivated(info);
}
}
}
void
TRoster::CheckSanity()
{
BAutolock _(fLock);
AppInfoList obsoleteApps;
for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) {
if (!(*it)->IsRunning())
obsoleteApps.AddInfo(*it);
}
for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
RemoveApp(*it);
delete *it;
}
obsoleteApps.MakeEmpty(false);
bigtime_t timeLimit = system_time() - kMaximalEarlyPreRegistrationPeriod;
for (AppInfoList::Iterator it = fEarlyPreRegisteredApps.It();
it.IsValid();
++it) {
if ((*it)->registration_time < timeLimit)
obsoleteApps.AddInfo(*it);
}
for (AppInfoList::Iterator it = obsoleteApps.It(); it.IsValid(); ++it) {
fEarlyPreRegisteredApps.RemoveInfo(*it);
delete *it;
}
obsoleteApps.MakeEmpty(false);
}
void
TRoster::SetShuttingDown(bool shuttingDown)
{
BAutolock _(fLock);
fShuttingDown = shuttingDown;
if (shuttingDown)
_SaveRosterSettings();
}
status_t
TRoster::GetShutdownApps(AppInfoList& userApps, AppInfoList& systemApps,
AppInfoList& backgroundApps, HashSet<HashKey32<team_id> >& vitalSystemApps)
{
BAutolock _(fLock);
status_t error = B_OK;
vitalSystemApps.Add(be_app->Team());
team_info teamInfo;
if (get_team_info(B_SYSTEM_TEAM, &teamInfo) == B_OK)
vitalSystemApps.Add(teamInfo.team);
RosterAppInfo* info
= fRegisteredApps.InfoFor("application/x-vnd.haiku-app_server");
if (info != NULL)
vitalSystemApps.Add(info->team);
info = fRegisteredApps.InfoFor("application/x-vnd.haiku-debug_server");
if (info != NULL)
vitalSystemApps.Add(info->team);
for (AppInfoList::Iterator it(fRegisteredApps.It());
RosterAppInfo* info = *it; ++it) {
if (!vitalSystemApps.Contains(info->team)) {
RosterAppInfo* clonedInfo = info->Clone();
if (clonedInfo) {
if (_IsSystemApp(info)) {
if (!systemApps.AddInfo(clonedInfo))
error = B_NO_MEMORY;
} else if (info->flags & B_BACKGROUND_APP) {
if (!backgroundApps.AddInfo(clonedInfo))
error = B_NO_MEMORY;
} else {
if (!userApps.AddInfo(clonedInfo))
error = B_NO_MEMORY;
}
if (error != B_OK)
delete clonedInfo;
} else
error = B_NO_MEMORY;
}
if (error != B_OK)
break;
}
info = fRegisteredApps.InfoFor("application/x-vnd.Be-input_server");
if (info != NULL)
vitalSystemApps.Add(info->team);
if (error != B_OK) {
userApps.MakeEmpty(true);
systemApps.MakeEmpty(true);
}
return error;
}
status_t
TRoster::AddAppInfo(AppInfoList& apps, team_id team)
{
BAutolock _(fLock);
for (AppInfoList::Iterator it(fRegisteredApps.It());
RosterAppInfo* info = *it; ++it) {
if (info->team == team) {
RosterAppInfo* clonedInfo = info->Clone();
status_t error = B_NO_MEMORY;
if (clonedInfo != NULL) {
if (!apps.AddInfo(clonedInfo))
delete clonedInfo;
else
error = B_OK;
}
return error;
}
}
return B_BAD_TEAM_ID;
}
status_t
TRoster::AddWatcher(Watcher* watcher)
{
BAutolock _(fLock);
if (!watcher)
return B_BAD_VALUE;
if (!fWatchingService.AddWatcher(watcher))
return B_NO_MEMORY;
return B_OK;
}
void
TRoster::RemoveWatcher(Watcher* watcher)
{
BAutolock _(fLock);
if (watcher)
fWatchingService.RemoveWatcher(watcher, false);
}
void
TRoster::_AppAdded(RosterAppInfo* info)
{
BMessage message(B_SOME_APP_LAUNCHED);
_AddMessageWatchingInfo(&message, info);
EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED);
fWatchingService.NotifyWatchers(&message, &filter);
}
void
TRoster::_AppRemoved(RosterAppInfo* info)
{
if (info) {
if (info == fActiveApp)
UpdateActiveApp(NULL);
BMessage message(B_SOME_APP_QUIT);
_AddMessageWatchingInfo(&message, info);
EventMaskWatcherFilter filter(B_REQUEST_QUIT);
fWatchingService.NotifyWatchers(&message, &filter);
}
}
void
TRoster::_AppActivated(RosterAppInfo* info)
{
if (info != NULL && info->state == APP_STATE_REGISTERED) {
BMessenger messenger;
BMessenger::Private messengerPrivate(messenger);
messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
BMessage message(B_APP_ACTIVATED);
message.AddBool("active", true);
MessageDeliverer::Default()->DeliverMessage(&message, messenger);
BMessage watcherMessage(B_SOME_APP_ACTIVATED);
_AddMessageWatchingInfo(&watcherMessage, info);
EventMaskWatcherFilter filter(B_REQUEST_ACTIVATED);
fWatchingService.NotifyWatchers(&watcherMessage, &filter);
}
}
void
TRoster::_AppDeactivated(RosterAppInfo* info)
{
if (info != NULL && info->state == APP_STATE_REGISTERED) {
BMessenger messenger;
BMessenger::Private messengerPrivate(messenger);
messengerPrivate.SetTo(info->team, info->port, B_NULL_TOKEN);
BMessage message(B_APP_ACTIVATED);
message.AddBool("active", false);
MessageDeliverer::Default()->DeliverMessage(&message, messenger);
}
}
status_t
TRoster::_AddMessageAppInfo(BMessage* message, const app_info* info)
{
flat_app_info flatInfo;
flatInfo.thread = info->thread;
flatInfo.team = info->team;
flatInfo.port = info->port;
flatInfo.flags = info->flags;
flatInfo.ref_device = info->ref.device;
flatInfo.ref_directory = info->ref.directory;
memcpy(flatInfo.signature, info->signature, B_MIME_TYPE_LENGTH);
flatInfo.ref_name[0] = '\0';
if (info->ref.name)
strcpy(flatInfo.ref_name, info->ref.name);
return message->AddData("app_info", B_REG_APP_INFO_TYPE, &flatInfo,
sizeof(flat_app_info));
}
status_t
TRoster::_AddMessageWatchingInfo(BMessage* message, const app_info* info)
{
status_t error = B_OK;
if (error == B_OK)
error = message->AddString("be:signature", info->signature);
if (error == B_OK)
error = message->AddInt32("be:team", info->team);
if (error == B_OK)
error = message->AddInt32("be:thread", info->thread);
if (error == B_OK)
error = message->AddInt32("be:flags", (int32)info->flags);
if (error == B_OK)
error = message->AddRef("be:ref", &info->ref);
return error;
}
uint32
TRoster::_NextToken()
{
return ++fLastToken;
}
void
TRoster::_AddIARRequest(IARRequestMap& map, int32 key, BMessage* request)
{
IARRequestMap::iterator it = map.find(key);
BMessageQueue* requests = NULL;
if (it == map.end()) {
requests = new(nothrow) BMessageQueue();
if (!requests) {
delete request;
return;
}
map[key] = requests;
} else
requests = it->second;
requests->AddMessage(request);
}
void
TRoster::_ReplyToIARRequests(BMessageQueue* requests, const RosterAppInfo* info)
{
while (BMessage* request = requests->NextMessage()) {
_ReplyToIARRequest(request, info);
delete request;
}
}
void
TRoster::_ReplyToIARRequest(BMessage* request, const RosterAppInfo* info)
{
bool preRegistered = false;
if (info) {
switch (info->state) {
case APP_STATE_PRE_REGISTERED:
preRegistered = true;
break;
case APP_STATE_UNREGISTERED:
case APP_STATE_REGISTERED:
preRegistered = false;
break;
}
}
BMessage reply(B_REG_SUCCESS);
reply.AddBool("registered", (bool)info);
reply.AddBool("pre-registered", preRegistered);
PRINT("_ReplyToIARRequest(): pre-registered: %d\n", preRegistered);
if (info)
_AddMessageAppInfo(&reply, info);
request->SendReply(&reply);
}
void
TRoster::_HandleGetRecentEntries(BMessage* request)
{
FUNCTION_START();
if (!request) {
D(PRINT("WARNING: TRoster::HandleGetRecentFolders(NULL) called\n"));
return;
}
int32 maxCount;
BMessage reply(B_REG_RESULT);
char** fileTypes = NULL;
int32 fileTypesCount = 0;
char* appSig = NULL;
status_t error = request->FindInt32("max count", &maxCount);
if (!error) {
type_code typeFound;
status_t typeError = request->GetInfo("file type", &typeFound,
&fileTypesCount);
if (!typeError)
typeError = typeFound == B_STRING_TYPE ? B_OK : B_BAD_TYPE;
if (!typeError) {
fileTypes = new(nothrow) char*[fileTypesCount];
typeError = fileTypes ? B_OK : B_NO_MEMORY;
}
if (!typeError) {
for (int i = 0; !error && i < fileTypesCount; i++) {
const char* type;
if (request->FindString("file type", i, &type) == B_OK) {
fileTypes[i] = new(nothrow) char[B_MIME_TYPE_LENGTH];
error = fileTypes[i] ? B_OK : B_NO_MEMORY;
BPrivate::Storage::to_lower(type, fileTypes[i]);
}
}
}
}
if (!error) {
const char* sig;
error = request->FindString("app sig", &sig);
if (!error) {
appSig = new(nothrow) char[B_MIME_TYPE_LENGTH];
error = appSig ? B_OK : B_NO_MEMORY;
BPrivate::Storage::to_lower(sig, appSig);
} else if (error == B_NAME_NOT_FOUND)
error = B_OK;
}
if (!error) {
switch (request->what) {
case B_REG_GET_RECENT_DOCUMENTS:
error = fRecentDocuments.Get(maxCount, (const char**)fileTypes,
fileTypesCount, appSig, &reply);
D(fRecentDocuments.Print());
break;
case B_REG_GET_RECENT_FOLDERS:
error = fRecentFolders.Get(maxCount, (const char**)fileTypes,
fileTypesCount, appSig, &reply);
D(fRecentFolders.Print());
break;
default:
D(PRINT("WARNING: TRoster::_HandleGetRecentEntries(): "
"unexpected request->what value of 0x%" B_PRIx32 "\n",
request->what));
error = B_BAD_VALUE;
break;
}
}
reply.AddInt32("result", error);
delete [] appSig;
if (fileTypes) {
for (int i = 0; i < fileTypesCount; i++)
delete [] fileTypes[i];
delete[] fileTypes;
fileTypes = NULL;
}
request->SendReply(&reply);
FUNCTION_END();
}
void
TRoster::_ValidateRunning(const entry_ref& ref, const char* signature)
{
while (true) {
RosterAppInfo* info = fRegisteredApps.InfoFor(&ref);
if (info == NULL && signature != NULL)
info = fRegisteredApps.InfoFor(signature);
if (info == NULL || info->IsRunning())
return;
RemoveApp(info);
delete info;
}
}
bool
TRoster::_IsSystemApp(RosterAppInfo* info) const
{
BPath path;
if (path.SetTo(&info->ref) != B_OK || path.GetParent(&path) != B_OK)
return false;
return !strcmp(path.Path(), fSystemAppPath.Path())
|| !strcmp(path.Path(), fSystemServerPath.Path());
}
status_t
TRoster::_LoadRosterSettings(const char* path)
{
BPath _path;
const char* settingsPath
= path ? path : get_default_roster_settings_path(_path, false);
RosterSettingsCharStream stream;
status_t error;
BFile file;
error = file.SetTo(settingsPath, B_READ_ONLY);
off_t size;
if (!error)
error = file.GetSize(&size);
char* data = NULL;
if (!error) {
data = new(nothrow) char[size + 1];
error = data ? B_OK : B_NO_MEMORY;
}
if (!error) {
ssize_t bytes = file.Read(data, size);
error = bytes < 0 ? bytes : (bytes == size ? B_OK : B_FILE_ERROR);
}
if (!error) {
data[size] = 0;
error = stream.SetTo(BString(data));
}
delete[] data;
if (!error) {
fRecentDocuments.Clear();
fRecentFolders.Clear();
fRecentApps.Clear();
while (true) {
status_t streamError;
char str[B_PATH_NAME_LENGTH];
streamError = stream.GetString(str);
if (!streamError) {
enum EntryType {
etDoc,
etFolder,
etApp,
etSomethingIsAmiss,
} type;
if (strcmp(str, "RecentDoc") == 0)
type = etDoc;
else if (strcmp(str, "RecentFolder") == 0)
type = etFolder;
else if (strcmp(str, "RecentApp") == 0)
type = etApp;
else
type = etSomethingIsAmiss;
switch (type) {
case etDoc:
case etFolder:
{
std::list<recent_entry*>* list = type == etDoc
? &fRecentDocuments.fEntryList
: &fRecentFolders.fEntryList;
char path[B_PATH_NAME_LENGTH];
char app[B_PATH_NAME_LENGTH];
char rank[B_PATH_NAME_LENGTH];
entry_ref ref;
ulong index = 0;
streamError = stream.GetString(path);
if (!streamError)
streamError = get_ref_for_path(path, &ref);
while (!streamError) {
if (!streamError)
streamError = stream.GetString(app);
if (!streamError) {
BPrivate::Storage::to_lower(app);
streamError = stream.GetString(rank);
}
if (!streamError) {
index = strtoul(rank, NULL, 10);
if (index == ULONG_MAX)
streamError = errno;
}
recent_entry* entry = NULL;
if (!streamError) {
entry = new(nothrow) recent_entry(&ref, app,
index);
streamError = entry ? B_OK : B_NO_MEMORY;
}
if (!streamError) {
D(printf("pushing entry, leaf == '%s', app == "
"'%s', index == %" B_PRId32 "\n",
entry->ref.name, entry->sig.c_str(),
entry->index));
list->push_back(entry);
}
}
if (streamError) {
D(printf("entry error 0x%" B_PRIx32 "\n",
streamError));
if (streamError
!= RosterSettingsCharStream::kEndOfLine
&& streamError
!= RosterSettingsCharStream::kEndOfStream)
stream.SkipLine();
}
break;
}
case etApp:
{
char app[B_PATH_NAME_LENGTH];
streamError = stream.GetString(app);
if (!streamError) {
BPrivate::Storage::to_lower(app);
fRecentApps.fAppList.push_back(app);
} else
stream.SkipLine();
break;
}
default:
stream.SkipLine();
break;
}
}
if (streamError == RosterSettingsCharStream::kEndOfStream)
break;
}
fRecentDocuments.fEntryList.sort(larger_index);
fRecentFolders.fEntryList.sort(larger_index);
D(
printf("----------------------------------------------------------------------\n");
fRecentDocuments.Print();
printf("----------------------------------------------------------------------\n");
fRecentFolders.Print();
printf("----------------------------------------------------------------------\n");
fRecentApps.Print();
printf("----------------------------------------------------------------------\n");
);
}
if (error) {
D(PRINT("WARNING: TRoster::_LoadRosterSettings(): error loading roster "
"settings from '%s', 0x%" B_PRIx32 "\n", settingsPath, error));
}
return error;
}
status_t
TRoster::_SaveRosterSettings(const char* path)
{
BPath _path;
const char* settingsPath
= path != NULL ? path : get_default_roster_settings_path(_path, true);
status_t error;
FILE* file;
file = fopen(settingsPath, "w+");
error = file ? B_OK : errno;
if (!error) {
status_t saveError;
saveError = fRecentDocuments.Save(file, "Recent documents", "RecentDoc");
if (saveError) {
D(PRINT("TRoster::_SaveRosterSettings(): recent documents save "
"failed with error 0x%" B_PRIx32 "\n", saveError));
}
saveError = fRecentFolders.Save(file, "Recent folders", "RecentFolder");
if (saveError) {
D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
"failed with error 0x%" B_PRIx32 "\n", saveError));
}
saveError = fRecentApps.Save(file);
if (saveError) {
D(PRINT("TRoster::_SaveRosterSettings(): recent folders save "
"failed with error 0x%" B_PRIx32 "\n", saveError));
}
fclose(file);
}
return error;
}