* Copyright 2001-2008, Ingo Weinhold, bonefish@users.sf.net.
* Distributed under the terms of the MIT License.
*/
applications.
*/
#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;
\brief Implements the application roster.
This class handles the BRoster requests. For each kind a hook method is
implemented to which the registrar looper dispatches the request messages.
Registered and pre-registered are managed via AppInfoLists.
\a fEarlyPreRegisteredApps contains the infos for those application that
are pre-registered and currently have no team ID assigned to them yet,
whereas the infos of registered and pre-registered applications with a
team ID are to be found in \a fRegisteredApps.
When an application asks whether it is pre-registered or not and there
are one or more instances of the application that are pre-registered, but
have no team ID assigned yet, the reply to the request has to be
postponed until the status of the requesting team is clear. The request
message is dequeued from the registrar's message queue and added to
\a fIARRequestsByID for a later reply.
The field \a fActiveApp identifies the currently active application
and \a fLastToken is a counter used to generate unique tokens for
pre-registered applications.
*/
const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
\param path BPath to be set to the roster settings path.
\param createDirectory makes sure the target directory exists if \c true.
\return the settings path as C string (\code path.Path() \endcode).
*/
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();
}
Also returns true if either entry is \c NULL.
Used for sorting the recent entry lists loaded from disk into the
proper order.
*/
bool
larger_index(const recent_entry* entry1, const recent_entry* entry2)
{
if (entry1 && entry2)
return entry1->index > entry2->index;
return true;
}
The object is completely initialized and ready to handle requests.
*/
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()
{
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
This is sent from the app_server when the current active application
is changed.
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
void
TRoster::HandleGetRecentDocuments(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
_HandleGetRecentEntries(request);
FUNCTION_END();
}
\param request The request message
*/
void
TRoster::HandleGetRecentFolders(BMessage* request)
{
FUNCTION_START();
BAutolock _(fLock);
_HandleGetRecentEntries(request);
FUNCTION_END();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
\param request The request message
*/
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();
}
Currently only adds the registrar to the roster.
The application must already be running, more precisly Run() must have
been called.
\return
- \c B_OK: Everything went fine.
- an error code
*/
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;
}
\param info The app info to be added
*/
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;
}
apps.
\param info The app info to be removed
*/
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);
}
}
}
}
The currently active application is deactivated and the one whose
info is supplied is activated. \a info may be \c NULL, which only
deactivates the currently active application.
\param info The info of the app to be activated
*/
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);
}
}
}
This is necessary, since killed applications don't unregister properly.
*/
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);
}
moment.
After this method is called with \a shuttingDown == \c true, no more
applications can be created.
\param shuttingDown \c true, to indicate the start of the shutdown process,
\c false to signalling its end.
*/
void
TRoster::SetShuttingDown(bool shuttingDown)
{
BAutolock _(fLock);
fShuttingDown = shuttingDown;
if (shuttingDown)
_SaveRosterSettings();
}
\param userApps List of RosterAppInfos identifying the user applications.
Those will be ask to quit first.
\param systemApps List of RosterAppInfos identifying the system applications
(like Tracker and Deskbar), which will be asked to quit after the
user applications are gone.
\param vitalSystemApps A set of team_ids identifying teams that must not
be terminated (app server and registrar).
\return \c B_OK, if everything went fine, another error code otherwise.
*/
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);
}
\param info The RosterAppInfo of the added application.
*/
void
TRoster::_AppAdded(RosterAppInfo* info)
{
BMessage message(B_SOME_APP_LAUNCHED);
_AddMessageWatchingInfo(&message, info);
EventMaskWatcherFilter filter(B_REQUEST_LAUNCHED);
fWatchingService.NotifyWatchers(&message, &filter);
}
removed.
\param info The RosterAppInfo of the removed application.
*/
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);
}
}
\param info The RosterAppInfo of the activated application.
*/
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);
}
}
\param info The RosterAppInfo of the deactivated application.
*/
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);
}
}
The info is added as a flat_app_info to a field "app_info" with the type
\c B_REG_APP_INFO_TYPE.
\param message The message
\param info The app_info.
\return \c B_OK if everything went fine, an error code otherwise.
*/
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));
}
\param message The message.
\param info The app_info of the concerned application.
\return \c B_OK if everything went fine, an error code otherwise.
*/
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;
}
\return The token.
*/
uint32
TRoster::_NextToken()
{
return ++fLastToken;
}
If something goes wrong, the method deletes the request.
\param map The map the request shall be added to.
\param key The key under which to add the request.
\param request The request message to be added.
*/
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);
}
message queue.
\param requests The request messages to be replied to
\param info The RosterAppInfo of the application in question
(may be \c NULL)
*/
void
TRoster::_ReplyToIARRequests(BMessageQueue* requests, const RosterAppInfo* info)
{
while (BMessage* request = requests->NextMessage()) {
_ReplyToIARRequest(request, info);
delete request;
}
}
The message to be sent is a simple \c B_REG_SUCCESS message containing
a "pre-registered" field, that says whether or not the application is
pre-registered. It will be set to \c false, unless an \a info is supplied
and the application this info refers to is pre-registered.
\param request The request message to be replied to
\param info The RosterAppInfo of the application in question
(may be \c NULL)
*/
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);
}
GetRecentFolders().
*/
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();
}
\brief Checks all registered apps for \a ref and \a signature if
they are still alive, and removes those that aren't.
*/
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(std::string(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;
}