Files
bongo/src/libs/msgapi/msgapi.c
T
2007-07-22 14:06:24 +00:00

1241 lines
36 KiB
C

/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
/* Product defines */
#define PRODUCT_NAME "Bongo API Library"
#define PRODUCT_VERSION "$Revision: 1.6 $"
#define PRODUCT_SHORT_NAME "msgapi.nlm"
#define CURRENT_PRODUCT_VERSION 3
#define CURRENT_PRODUCT_VERSION_MAJOR 5
#define CURRENT_PRODUCT_VERSION_MINOR 3
#include <config.h>
#include <xpl.h>
#include <xplresolve.h>
#include <connio.h>
#include <nmap.h>
#include <nmlib.h>
#include <msgapi.h>
#include "msgapip.h"
#include <bongo-buildinfo.h>
#include <bongoutil.h>
#include <bongostore.h>
#include <curl/curl.h>
#define MSGAPI_FLAG_EXITING (1 << 0)
#define MSGAPI_FLAG_STANDALONE (1 << 1)
#define MSGAPI_FLAG_SUPPORT_CONNMGR (1 << 2)
#define MSGAPI_FLAG_CLUSTERED (1 << 3)
#define MSGAPI_FLAG_BOUND (1 << 4)
#define MSGAPI_FLAG_INTERNET_EMAIL_ADDRESS (1 << 5)
#define MSGAPI_FLAG_MONITOR_RUNNING (1 << 6)
#define LOOPBACK_ADDRESS 0x0100007F
#define EMPTY_CHAR 0x01
#define CONNMGR_STRUCT_SIZE (sizeof(ConnectionManagerCommand) + MAXEMAILNAMESIZE)
#define CONNMGR_REPLY_NOT_FOUND 0
#define CONNMGR_REPLY_FOUND 1
#define CONNMGR_REPLY_FOUND_NAME 2
#define CONNMGR_REQ_STORE 0
#define CONNMGR_REQ_CHECK 1
#define CONNMGR_REQ_STORE_NAME 2
#define CONNMGR_REQ_CHECK_NAME 3
enum LibraryStates {
LIBRARY_LOADED = 0,
LIBRARY_INITIALIZING,
LIBRARY_RUNNING,
LIBRARY_SHUTTING_DOWN,
LIBRARY_SHUTDOWN,
LIBRARY_MAX_STATES
} MSGAPIState = LIBRARY_LOADED;
struct {
unsigned long flags;
MDBHandle directoryHandle;
XplThreadID groupID;
struct {
XplSemaphore version;
XplSemaphore shutdown;
XplMutex uid;
} sem;
struct {
unsigned char dn[MDB_MAX_OBJECT_CHARS + 1];
} server;
struct {
MDBValueStruct *storePaths;
MDBValueStruct *storeDNs;
struct {
MDBValueStruct *names;
MDBValueStruct *contexts;
struct sockaddr_in *addr;
} server;
#ifdef PARENTOBJECT_STORE
struct {
MDBValueStruct *objects;
MDBValueStruct *stores;
MDBValueSTruct *indices;
} parent;
#endif
} vs;
unsigned long connManager;
XplRWLock configLock;
struct {
unsigned char work[XPL_MAX_PATH + 1];
unsigned char nls[XPL_MAX_PATH + 1];
unsigned char dbf[XPL_MAX_PATH + 1];
unsigned char bin[XPL_MAX_PATH + 1];
unsigned char lib[XPL_MAX_PATH + 1];
unsigned char certificate[XPL_MAX_PATH + 1];
unsigned char key[XPL_MAX_PATH + 1];
} paths;
struct {
unsigned long local;
unsigned char string[16];
} address;
struct {
unsigned char domain[MAXEMAILNAMESIZE + 1];
unsigned long domainLength;
} official;
XplAtomic useCount;
} MsgGlobal;
typedef struct _ConnectionManagerCommand {
unsigned long request;
unsigned long address;
unsigned long nameLen;
unsigned long counter;
unsigned char name[1];
} ConnectionManagerCommand;
/* private */
BOOL
MsgGetUpdateStatus(char *record, int record_length);
/* I tried to remove this, but it's still needed :(
* Python server for Dragonfly needs a way to authenticate itself
* to the queue...
*/
void
MsgNmapChallenge(const unsigned char *response, unsigned char *reply, size_t length)
{
unsigned char *ptr;
unsigned char *salt;
static unsigned char access[NMAP_HASH_SIZE] = { '\0' };
MDBValueStruct *v;
xpl_hash_context ctx;
if (access[0] == '\0') {
if (MsgGlobal.directoryHandle) {
v = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
if (v) {
MDBRead(MSGSRV_ROOT, MSGSRV_A_ACL, v);
if (v->Used) {
HashCredential(MsgGlobal.server.dn, v->Value[0], access);
}
MDBDestroyValueStruct(v);
}
}
}
if (access[0] && reply && (length > 32) && ((ptr = strchr(response, '<')) != NULL)) {
salt = ++ptr;
if ((ptr = strchr(ptr, '>')) != NULL) {
*ptr = '\0';
}
XplHashNew(&ctx, XPLHASH_MD5);
XplHashWrite(&ctx, salt, strlen(salt));
XplHashWrite(&ctx, access, NMAP_HASH_SIZE);
XplHashFinal(&ctx, XPLHASH_LOWERCASE, reply, XPLHASH_MD5_LENGTH);
} else if (reply) {
*reply = '\0';
}
return;
}
EXPORT const unsigned char *
MsgGetServerDN(unsigned char *buffer)
{
if (buffer) {
return(strcpy(buffer, MsgGlobal.server.dn));
}
return(MsgGlobal.server.dn);
}
EXPORT BOOL
MsgFindObject(const unsigned char *user, unsigned char *dn, unsigned char *userType, struct sockaddr_in *nmap, MDBValueStruct *v)
{
BOOL retVal = FALSE;
MDBValueStruct *userCtx;
MDBValueStruct *types;
unsigned long i, j;
unsigned char rdn[MDB_MAX_OBJECT_CHARS + 1];
BOOL isUser = FALSE;
BOOL isGroup = FALSE;
BOOL isResource = FALSE;
BOOL isOrgRole = FALSE;
BOOL isDynGroup = FALSE;
/* FIXME: handle failure to create ValueStructs */
userCtx = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
types = MDBShareContext(userCtx);
XplRWReadLockAcquire(&MsgGlobal.configLock);
for (i = 0; i < MsgGlobal.vs.server.contexts->Used; i++) {
MDBSetValueStructContext(MsgGlobal.vs.server.contexts->Value[i], userCtx);
if (MDBGetObjectDetailsEx(user, types, rdn, dn, userCtx)) {
/* determine relevant types now so we don't have to loop
throught the list everytime. */
for (j = 0; j < types->Used; j++) {
if (strcmp(types->Value[j], C_USER) == 0) {
isUser = TRUE;
} else if (strcmp(types->Value[j],
MSGSRV_C_RESOURCE) == 0) {
isResource = TRUE;
} else if (strcmp(types->Value[j], MSGSRV_C_GROUP) == 0 ||
strcmp(types->Value[j], C_GROUP) == 0) {
isGroup = TRUE;
} else if (strcmp(types->Value[j],
MSGSRV_C_ORGANIZATIONAL_ROLE) == 0 ||
strcmp(types->Value[j],
C_ORGANIZATIONAL_ROLE) == 0) {
isOrgRole = TRUE;
} else if (strcmp(types->Value[j], "dynamicGroup") == 0) {
isDynGroup = TRUE;
}
}
if (isUser || isResource) {
if (v) {
MDBAddValue(rdn, v);
}
if (nmap) {
memcpy(nmap, &MsgGlobal.vs.server.addr[i], sizeof(struct sockaddr_in));
}
if (userType) {
if (isUser) {
strcpy(userType, C_USER);
} else if (isResource) {
strcpy(userType, MSGSRV_C_RESOURCE);
}
}
retVal = TRUE;
} else if (isGroup) {
if (v) {
MDBSetValueStructContext(MsgGlobal.vs.server.contexts->Value[i], v);
MDBRead(user, A_MEMBER, v);
/* This removes any context from a group member's name */
for (j = 0; j < v->Used; j++) {
unsigned char *ptr;
ptr = strrchr(v->Value[j], '\\');
if (ptr) {
memmove(v->Value[j], ptr + 1, strlen(ptr + 1) + 1);
}
}
}
if (userType) {
strcpy(userType, C_GROUP);
}
retVal = TRUE;
} else if (isOrgRole) {
if (v) {
MDBSetValueStructContext(MsgGlobal.vs.server.contexts->Value[i], v);
MDBRead(user, A_ROLE_OCCUPANT, v);
/* This removes any context from a the roles name */
for (j = 0; j < v->Used; j++) {
unsigned char *ptr;
ptr = strrchr(v->Value[j], '\\');
if (ptr) {
memmove(v->Value[j], ptr + 1, strlen(ptr + 1) + 1);
}
}
}
if (userType) {
strcpy(userType, C_ORGANIZATIONAL_ROLE);
}
retVal = TRUE;
} else if (isDynGroup) {
if (v) {
MDBSetValueStructContext(MsgGlobal.vs.server.contexts->Value[i], v);
MDBRead(user, A_MEMBER, v);
/* This removes any context from a group member's name */
for (j = 0; j < v->Used; j++) {
unsigned char *ptr;
ptr=strrchr(v->Value[j], '\\');
if (ptr) {
memmove(v->Value[j], ptr + 1, strlen(ptr + 1) + 1);
}
}
}
if (userType) {
strcpy(userType, "dynamicGroup");
}
retVal = TRUE;
}
break;
}
}
XplRWReadLockRelease(&MsgGlobal.configLock);
MDBDestroyValueStruct(userCtx);
MDBDestroyValueStruct(types);
return(retVal);
}
EXPORT BOOL
MsgReadIP(const unsigned char *object, unsigned char *type, MDBValueStruct *v)
{
int first,last, i;
unsigned char *ptr;
if (!v || !type) {
return(FALSE);
}
first=v->Used;
if (!object) {
if (!MDBReadDN(MsgGlobal.server.dn, type, v)) {
return(FALSE);
}
} else {
if (!MDBReadDN(object, type, v)) {
return(FALSE);
}
}
last = v->Used;
for (i = first; i < last; i++) {
if ((ptr = strrchr(v->Value[i], '\\')) != NULL) {
*ptr = '\0';
}
MDBRead(v->Value[i], MSGSRV_A_IP_ADDRESS, v);
}
/* The following loop removes the names read by the ReadDN above,
* but keeps the IP addresses added afterwards :-) */
for (i = first; i < last; i++) {
MDBFreeValue(0, v);
}
if (v->Used > 0) {
return(TRUE);
} else {
return(FALSE);
}
}
EXPORT BOOL
MsgDomainExists(const unsigned char *domain, unsigned char *domainObjectDN)
{
/* FIXME later; when we handle parent objects */
return(FALSE);
}
/*
* Use these functions to ensure that the correct MDB function is used
* for strings or DNs.
*/
static BOOL
MsgMdbWriteAny(const unsigned char *dn, const unsigned char *attribute,
MDBValueStruct *v)
{
if (v->Used > 0 && strchr(v->Value[0], '\\')) {
return MDBWriteDN(dn, attribute, v);
}
return MDBWrite(dn, attribute, v);
}
static BOOL
MsgMdbAddAny(const unsigned char *dn, const unsigned char *attribute,
const unsigned char *value, MDBValueStruct *v)
{
if (v->Used > 0 && strchr(v->Value[0], '\\')) {
return MDBAddDN(dn, attribute, value, v);
}
return MDBAdd(dn, attribute, value, v);
}
EXPORT BOOL
MsgCleanPath(unsigned char *path)
{
unsigned char *ptr;
unsigned char *ptr2;
/* This code strips off any server names that might be configured in the Path */
ptr = path;
while((ptr = strchr(ptr, '\\')) != NULL) {
*ptr = '/';
}
ptr=strchr(path, ':');
if (ptr) {
*ptr = '\0';
ptr2 = strchr(path, '/');
if (ptr2) {
*ptr=':';
memmove(path, ptr2+1, strlen(ptr2+1)+1);
} else {
*ptr=':';
}
}
/* If you want all pathnames lowercase, uncomment the following */
#if 0
ptr = path;
while (*ptr) {
if (isupper(*ptr)) {
*ptr = tolower(*ptr);
}
ptr++;
}
#endif
/* strip off trailing /, to allow other code reliable appending of files by using %s/%s */
ptr = path + strlen(path) - 1;
if (*ptr == '/') {
*ptr = '\0';
}
return(TRUE);
}
EXPORT void
MsgMakePath(unsigned char *path)
{
unsigned char *ptr = path;
unsigned char *ptr2;
struct stat sb;
ptr = strchr(path, '/');
if (!ptr)
ptr=strchr(path, '\\');
while (ptr) {
*ptr = '\0';
if (stat(path, &sb) != 0) {
XplMakeDir(path);
}
*ptr = '/';
ptr2 = ptr;
ptr = strchr(ptr2 + 1, '/');
if (!ptr)
ptr = strchr(ptr2 + 1, '\\');
}
if (stat(path, &sb) != 0) {
XplMakeDir(path);
}
}
EXPORT const unsigned char *
MsgGetDBFDir(char *directory)
{
if (directory) {
strcpy(directory, MsgGlobal.paths.dbf);
}
return(MsgGlobal.paths.dbf);
}
EXPORT const unsigned char *
MsgGetWorkDir(char *directory)
{
if (directory) {
strcpy(directory, MsgGlobal.paths.work);
}
return(MsgGlobal.paths.work);
}
EXPORT const unsigned char *
MsgGetNLSDir(char *directory)
{
if (directory) {
strcpy(directory, MsgGlobal.paths.nls);
}
return(MsgGlobal.paths.nls);
}
EXPORT const unsigned char *
MsgGetBinDir(char *directory)
{
if (directory) {
strcpy(directory, MsgGlobal.paths.bin);
}
return(MsgGlobal.paths.bin);
}
EXPORT const unsigned char *
MsgGetLibDir(char *directory)
{
if (directory) {
strcpy(directory, MsgGlobal.paths.lib);
}
return(MsgGlobal.paths.bin);
}
EXPORT const unsigned char *
MsgGetTLSCertPath(char *path)
{
if (path) {
strcpy(path, MsgGlobal.paths.certificate);
}
return(MsgGlobal.paths.certificate);
}
EXPORT const unsigned char *
MsgGetTLSKeyPath(char *path)
{
if (path) {
strcpy(path, MsgGlobal.paths.key);
}
return(MsgGlobal.paths.key);
}
EXPORT unsigned long
MsgGetAgentBindIPAddress(void)
{
if ((MsgGlobal.flags & MSGAPI_FLAG_BOUND)) {
return(MsgGlobal.address.local);
} else {
return(0);
}
}
EXPORT unsigned long
MsgGetHostIPAddress(void)
{
return(MsgGlobal.address.local);
}
EXPORT const char *
MsgGetUnprivilegedUser(void)
{
if (BONGO_USER[0] == '\0') {
return NULL;
} else {
return BONGO_USER;
}
}
static BOOL
LoadContextList(MDBValueStruct *config)
{
unsigned char emptyString[] = " ";
unsigned char storeObjectDn[MDB_MAX_OBJECT_CHARS + 1];
MDBValueStruct *ports;
MDBValueStruct *addresses;
char storePortText[6];
unsigned long i;
unsigned long j;
unsigned long count;
unsigned long realUsed;
ports = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
if (!ports) {
return(FALSE);
}
addresses = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
if (!addresses) {
MDBDestroyValueStruct(ports);
return(FALSE);
}
MDBFreeValues(MsgGlobal.vs.server.names);
MDBFreeValues(MsgGlobal.vs.server.contexts);
MDBFreeValues(MsgGlobal.vs.storePaths);
MDBFreeValues(MsgGlobal.vs.storeDNs);
if (MsgGlobal.vs.server.addr) {
MemFree(MsgGlobal.vs.server.addr);
MsgGlobal.vs.server.addr = NULL;
}
/* This puts the local context always first */
sprintf(storeObjectDn, "%s\\%s", MsgGlobal.server.dn, MSGSRV_AGENT_STORE);
if(MDBIsObject(storeObjectDn, MsgGlobal.vs.server.contexts)) {
MDBReadDN(MsgGlobal.server.dn, MSGSRV_A_CONTEXT, MsgGlobal.vs.server.contexts);
if (MDBRead(storeObjectDn, MSGSRV_A_PORT, config)) {
strcpy(storePortText, config->Value[0]);
MDBFreeValues(config);
} else {
sprintf(storePortText, "%d", (BONGO_STORE_DEFAULT_PORT));
}
for (i = 0; i < MsgGlobal.vs.server.contexts->Used; i++) {
MDBRead(MsgGlobal.server.dn, MSGSRV_A_IP_ADDRESS, addresses);
MDBAddValue(storePortText, ports);
MDBAddValue(MsgGlobal.server.dn, MsgGlobal.vs.storeDNs);
}
}
#ifdef PARENTOBJECT_STORE
/* Read the parent object store configuration */
if (MDBEnumerateObjects(MSGSRV_ROOT"\\"MSGSRV_PARENT_ROOT, MSGSRV_C_PARENTOBJECT, &MsgGlobal.vs.parent.objects)) {
for (i=0; i<MsgGlobal.vs.parent.objects.Used; i++) {
if (MDBRead(MsgGlobal.vs.parent.objects.Value[i], MSGSRV_A_MESSAGE_STORE, &MsgGlobal.vs.parent.stores)==0) {
MDBAddValue(EmptyString, &MsgGlobal.vs.parent.stores);
}
}
}
#endif
/* FIXME: This code turns off distributed automatically if the server is not in internet services */
/* We might not always want this? */
#if 0
if (strstr(storeObjectDn, MSGSRV_ROOT)==NULL) {
MsgGlobal.flags |= MSGAPI_FLAG_STANDALONE;
}
#endif
if (!(MsgGlobal.flags & MSGAPI_FLAG_STANDALONE)) {
/* First time around we check all "real" classes */
if (MDBEnumerateObjects(MSGSRV_ROOT, MSGSRV_C_SERVER, NULL, MsgGlobal.vs.server.names)) {
/* Put the local context(s) first */
for (i = 0; i < MsgGlobal.vs.server.names->Used; i++) {
/* MsgGlobal.server.dn is absolute, MsgGlobal.vs.server.names->Value isn't */
if (XplStrCaseCmp(MsgGlobal.server.dn + strlen(MsgGlobal.server.dn) - strlen(MsgGlobal.vs.server.names->Value[i]), MsgGlobal.vs.server.names->Value[i]) != 0) {
count = MsgGlobal.vs.server.contexts->Used;
if ((MDBRead(MsgGlobal.vs.server.names->Value[i], MSGSRV_A_IP_ADDRESS, addresses) > 0) &&
(MDBReadDN(MsgGlobal.vs.server.names->Value[i], MSGSRV_A_CONTEXT, MsgGlobal.vs.server.contexts) > 0)) {
sprintf(storeObjectDn, "%s\\%s", MsgGlobal.vs.server.names->Value[i], MSGSRV_AGENT_STORE);
if (MDBRead(storeObjectDn, MSGSRV_A_PORT, config)) {
strcpy(storePortText, config->Value[0]);
MDBFreeValues(config);
} else {
sprintf(storePortText, "%d", (BONGO_STORE_DEFAULT_PORT));
}
MDBAddValue(MsgGlobal.vs.server.names->Value[i], MsgGlobal.vs.storeDNs);
for (j = count + 1; j < MsgGlobal.vs.server.contexts->Used; j++) {
MDBAddValue(addresses->Value[count], addresses);
MDBAddValue(storePortText, ports);
MDBAddValue(MsgGlobal.vs.server.names->Value[i], MsgGlobal.vs.storeDNs);
}
}
}
}
}
/* Now check any aliases that might be in the Internet Services container */
realUsed = MsgGlobal.vs.server.names->Used;
if (MDBEnumerateObjects(MSGSRV_ROOT, C_ALIAS, NULL, MsgGlobal.vs.server.names)) {
unsigned char realDn[MDB_MAX_OBJECT_CHARS + 1];
unsigned char realType[MDB_MAX_ATTRIBUTE_CHARS + 1];
/* Put the local context(s) first */
for (i = realUsed; i < MsgGlobal.vs.server.names->Used; i++) {
MDBGetObjectDetails(MsgGlobal.vs.server.names->Value[i], realType, NULL, realDn, MsgGlobal.vs.server.names);
/* MsgGlobal.server.dn is absolute, MsgGlobal.vs.server.names->Value isn't */
if ((XplStrCaseCmp(realType, MSGSRV_C_SERVER) == 0) && (XplStrCaseCmp(MsgGlobal.server.dn + strlen(MsgGlobal.server.dn) - strlen(realDn), realDn) != 0)) {
count = MsgGlobal.vs.server.contexts->Used;
if ((MDBRead(MsgGlobal.vs.server.names->Value[i], MSGSRV_A_IP_ADDRESS, addresses) > 0) &&
(MDBReadDN(MsgGlobal.vs.server.names->Value[i], MSGSRV_A_CONTEXT, MsgGlobal.vs.server.contexts) > 0)) {
sprintf(storeObjectDn, "%s\\%s", MsgGlobal.vs.server.names->Value[i], MSGSRV_AGENT_STORE);
if (MDBRead(storeObjectDn, MSGSRV_A_PORT, config)) {
strcpy(storePortText, config->Value[0]);
MDBFreeValues(config);
} else {
sprintf(storePortText, "%d", BONGO_STORE_DEFAULT_PORT);
}
MDBAddValue(MsgGlobal.vs.server.names->Value[i], MsgGlobal.vs.storeDNs);
for (j = count + 1; j < MsgGlobal.vs.server.contexts->Used; j++) {
MDBAddValue(addresses->Value[count], addresses);
MDBAddValue(storePortText, ports);
MDBAddValue(MsgGlobal.vs.server.names->Value[i], MsgGlobal.vs.storeDNs);
}
}
}
}
}
}
/* MDBAddValue doesn't add empty values, so fake it */
/* with a ^A in the string */
emptyString[0]=EMPTY_CHAR;
/* Now read all the store path values */
for (i = 0; i < MsgGlobal.vs.server.contexts->Used; i++) {
if (MDBRead(MsgGlobal.vs.server.contexts->Value[i], MSGSRV_A_MESSAGE_STORE, MsgGlobal.vs.storePaths) == 0) {
/* Add an empty string, no context store specified */
MDBAddValue(emptyString, MsgGlobal.vs.storePaths);
} else {
MsgCleanPath(MsgGlobal.vs.storePaths->Value[i]);
}
}
/* Initialize addr structure for each context */
MsgGlobal.vs.server.addr = MemMalloc0(MsgGlobal.vs.server.contexts->Used * sizeof(struct sockaddr_in));
if (!MsgGlobal.vs.server.addr) {
MDBDestroyValueStruct(ports);
return(FALSE);
}
for (i = 0; i < MsgGlobal.vs.server.contexts->Used; i++) {
MsgGlobal.vs.server.addr[i].sin_addr.s_addr = inet_addr(addresses->Value[i]);
MsgGlobal.vs.server.addr[i].sin_port = htons(atol(ports->Value[i]));
MsgGlobal.vs.server.addr[i].sin_family = AF_INET;
}
#if 0
for (i = 0; i < MsgGlobal.vs.server.contexts->Used; i++) {
XplConsolePrintf("\rCtx:%-35s IP:%-15s Path:%s\n", MsgGlobal.vs.server.contexts->Value[i], addresses->Value[i], MsgGlobal.vs.storePaths->Value[i]);
XplConsolePrintf("\rDN :%-35s\n\n", MsgGlobal.vs.storeDNs->Value[i]);
}
#endif
MDBDestroyValueStruct(addresses);
MDBDestroyValueStruct(ports);
return(TRUE);
}
static BOOL
MsgLibraryStop(void)
{
while (MsgGlobal.flags & MSGAPI_FLAG_MONITOR_RUNNING) {
XplDelay(1000);
}
MDBDestroyValueStruct(MsgGlobal.vs.storeDNs);
MDBDestroyValueStruct(MsgGlobal.vs.storePaths);
MDBDestroyValueStruct(MsgGlobal.vs.server.contexts);
MDBDestroyValueStruct(MsgGlobal.vs.server.names);
if (MsgGlobal.vs.server.addr) {
MemFree(MsgGlobal.vs.server.addr);
MsgGlobal.vs.server.addr = NULL;
}
#ifdef PARENTOBJECT_STORE
MDBDestroyValueStruct(MsgGlobal.vs.parent.stores);
MDBDestroyValueStruct(MsgGlobal.vs.parent.objects);
#endif
XplRWLockDestroy(&MsgGlobal.configLock);
return(TRUE);
}
/**
* Get a config property from global configuration file. Removes dependency
* on NCPServer object in the directory (as well as others). See msgapi.h for
* possible properties.
*/
BOOL
MsgGetConfigProperty(unsigned char *Buffer, unsigned char *Property)
{
int result = FALSE;
char line[512];
char *ptr;
FILE *fh;
int len;
char conf_path[FILENAME_MAX];
snprintf(conf_path, FILENAME_MAX, "%s/%s", XPL_DEFAULT_CONF_DIR,
MSGSRV_CONFIG_FILENAME);
if (!(fh = fopen(conf_path, "r"))) {
return result;
}
while (fgets(line, 512, fh)) {
if (line[0] != '#') {
if ((ptr = strchr(line, '='))) {
*ptr++ = '\0';
len = strlen(ptr);
if (!strcmp(line, Property)) {
if (ptr[len - 1] == '\n') {
ptr[len - 1] = '\0';
}
strncpy(Buffer, ptr, MSGSRV_CONFIG_MAX_PROP_CHARS);
result = TRUE;
break;
}
} else {
break;
}
}
}
fclose(fh);
return result;
}
void
MsgGetUid(char *buffer, int buflen)
{
static int serial = 0;
static char hostname[512] = "";
int thisSerial;
XplMutexLock(MsgGlobal.sem.uid);
if (hostname[0] == '\0') {
if ((gethostname(hostname, sizeof(hostname) - 1) != 0) ||
hostname[0] == '\0') {
strcpy(hostname, "localhost");
}
}
thisSerial = serial++;
XplMutexUnlock(MsgGlobal.sem.uid);
snprintf(buffer, buflen, "%lu.%lu.%d@%s",
(unsigned long) time(NULL),
(unsigned long) getpid(),
thisSerial,
hostname);
}
static BOOL
MsgLibraryStart(void)
{
unsigned long i;
struct sockaddr_in server_sockaddr;
unsigned char path[XPL_MAX_PATH + 1];
MDBValueStruct *config;
XplThreadID ID;
/* Prepare later config updates */
XplRWLockInit(&MsgGlobal.configLock);
/* Read generic configuration info */
MsgGlobal.flags |= MSGAPI_FLAG_SUPPORT_CONNMGR;
// MsgGlobal.flags |= MSGAPI_FLAG_CLUSTERED;
// 'BOUND' conflicts with 'CLUSTERED' ?
// MsgGlobal.flags |= MSGAPI_FLAG_BOUND;
strcpy(MsgGlobal.paths.certificate, XPL_DEFAULT_CERT_PATH);
strcpy(MsgGlobal.paths.key, XPL_DEFAULT_KEY_PATH);
/* Read "context" related stuff */
MsgGlobal.vs.server.names = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
MsgGlobal.vs.server.contexts = MDBShareContext(MsgGlobal.vs.server.names);
MsgGlobal.vs.storePaths = MDBShareContext(MsgGlobal.vs.server.names);
MsgGlobal.vs.storeDNs = MDBShareContext(MsgGlobal.vs.server.names);
MsgGlobal.vs.server.addr = NULL;
#ifdef PARENTOBJECT_STORE
MsgGlobal.vs.parent.objects = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
MsgGlobal.vs.parent.stores = MDBShareContext(MsgGlobal.vs.parent.objects);
#endif
server_sockaddr.sin_addr.s_addr=XplGetHostIPAddress();
sprintf(MsgGlobal.address.string,"%d.%d.%d.%d",
server_sockaddr.sin_addr.s_net,
server_sockaddr.sin_addr.s_host,
server_sockaddr.sin_addr.s_lh,
server_sockaddr.sin_addr.s_impno);
MDBAddValue(MsgGlobal.address.string, MsgGlobal.vs.server.names);
if (!(MsgGlobal.flags & MSGAPI_FLAG_CLUSTERED)) {
MDBWrite(MsgGlobal.server.dn, MSGSRV_A_IP_ADDRESS, MsgGlobal.vs.server.names);
} else {
MDBFreeValues(MsgGlobal.vs.server.names);
if (MDBRead(MsgGlobal.server.dn, MSGSRV_A_IP_ADDRESS, MsgGlobal.vs.server.names)>0) {
strcpy(MsgGlobal.address.string, MsgGlobal.vs.server.names->Value[0]);
}
}
MDBFreeValues(MsgGlobal.vs.server.names);
MsgGlobal.address.local = inet_addr(MsgGlobal.address.string);
/* This stuff is here to prevent a catch-22 with the IP address configuration above */
if (MsgReadIP(MsgGlobal.server.dn, MSGSRV_A_CONNMGR, config)) {
MsgGlobal.connManager = inet_addr(config->Value[0]);
}
MDBFreeValues(config);
LoadContextList(config);
MDBDestroyValueStruct(config);
return(TRUE);
}
MDBHandle
MsgGetSystemDirectoryHandle(void)
{
MDBHandle systemHandle;
unsigned char buffer[XPL_MAX_PATH + 1];
unsigned char credential[128];
FILE *eclients;
memset(credential, 0, sizeof(credential));
sprintf(buffer, "%s/eclients.dat", XPL_DEFAULT_DBF_DIR);
eclients = fopen(buffer, "rb");
if (eclients) {
fread(credential, sizeof(unsigned char), sizeof(credential), eclients);
fclose(eclients);
eclients = NULL;
} else {
XplConsolePrintf("Insufficient privileges; shutting down.\n");
return(NULL);
}
if (!MsgGetConfigProperty(MsgGlobal.server.dn,
MSGSRV_CONFIG_PROP_MESSAGING_SERVER)) {
XplConsolePrintf("Messaging server not configured. Shutdown.\n");
return(NULL);
}
systemHandle = MDBAuthenticate("Bongo", MsgGlobal.server.dn, credential);
if (systemHandle == NULL) {
XplConsolePrintf("Messaging server credentials are invalid; shutting down.\n");
return(NULL);
}
return(systemHandle);
}
BOOL
MsgGetBuildVersion(int *version, BOOL *custom)
{
/* Get the version of this build of bongo.
* version - what SVN rev or branch minor this is
* custom - whether or not this is a custom build
*/
int len = 0;
char *ptr = BONGO_BUILD_VER;
*version = strtol(ptr, NULL, 10);
len = strlen(ptr);
*custom = FALSE;
if (ptr[len] == 'M') *custom = TRUE;
return TRUE;
}
BOOL
MsgGetAvailableVersion(int *version)
{
/* Get the information about latest version of bongo available
* version - what SVN rev or branch minor is available
*/
char record[500];
char *ptr;
int v;
if (!MsgGetUpdateStatus(record, 499))
return FALSE;
ptr = strchr(record, '|');
if (ptr == NULL) return FALSE;
*version = strtol(record, &ptr, 10);
return TRUE;
}
BOOL
MsgGetUpdateStatus(char *record, int record_length)
{
char *branch = BONGO_BUILD_BRANCH;
char *zone = "revisions.bongo-project.com";
char hostname[256];
XplDnsRecord *result = NULL;
int status;
snprintf(hostname, 255, "%s.%s", branch, zone);
status = XplDnsResolve(hostname, &result, XPL_RR_TXT);
switch(status) {
case XPL_DNS_SUCCESS:
snprintf(record, record_length, "%s", result[0].TXT.txt);
MemFree(result);
return TRUE;
break;
default:
return FALSE;
}
}
static BOOL
MsgReadConfiguration(void)
{
MDBValueStruct *config;
unsigned long i;
struct tm *timeStruct;
time_t utcTime;
long tmp;
/* Get Server Time Zone Offset */
tzset();
utcTime = time(NULL);
timeStruct = localtime(&utcTime);
if (timeStruct) {
tmp = (((((((timeStruct->tm_year - 70) * 365) + timeStruct->tm_yday) * 24) + timeStruct->tm_hour) * 60) + timeStruct->tm_min) * 60;
timeStruct = gmtime(&utcTime);
tmp -= (((((((timeStruct->tm_year - 70) * 365) + timeStruct->tm_yday) * 24) + timeStruct->tm_hour) * 60) + timeStruct->tm_min) * 60;
MsgDateSetUTCOffset(tmp);
}
/* Read operating parameters, this is so complicated because in version 2.5
* we changed the configuration of directories - before 2.5 we would always
* append novonyx/mail to any given path, now we don't. The code tries to
* automatically detect pre2.5 installs and change the DS attribute...
*/
config = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
if (!config) {
XplConsolePrintf("Messaging server out of memory; shutting down.\n");
MDBRelease(MsgGlobal.directoryHandle);
MsgGlobal.directoryHandle = NULL;
return(FALSE);
}
strcpy(MsgGlobal.paths.dbf, XPL_DEFAULT_DBF_DIR);
MsgMakePath(MsgGlobal.paths.dbf);
strcpy(MsgGlobal.paths.bin, XPL_DEFAULT_BIN_DIR);
MsgMakePath(MsgGlobal.paths.bin);
strcpy(MsgGlobal.paths.lib, XPL_DEFAULT_LIB_DIR);
MsgMakePath(MsgGlobal.paths.lib);
strcpy(MsgGlobal.paths.work, XPL_DEFAULT_WORK_DIR);
MsgMakePath(MsgGlobal.paths.work);
strcpy(MsgGlobal.paths.nls, XPL_DEFAULT_NLS_DIR);
MsgMakePath(MsgGlobal.paths.nls);
strcpy(MsgGlobal.paths.work, XPL_DEFAULT_WORK_DIR);
MsgMakePath(MsgGlobal.paths.work);
/* Official Name */
if (MDBRead(MsgGlobal.server.dn, MSGSRV_A_OFFICIAL_NAME, config)) {
strcpy(MsgGlobal.official.domain, config->Value[0]);
MsgGlobal.official.domainLength = strlen(MsgGlobal.official.domain);
}
MDBFreeValues(config);
if (MDBRead(MsgGlobal.server.dn, MSGSRV_A_SERVER_STANDALONE, config)) {
if (config->Value[0][0]=='1') {
MsgGlobal.flags |= MSGAPI_FLAG_STANDALONE;
}
}
MDBDestroyValueStruct(config);
return(TRUE);
}
static int
MsgLibraryInit(void)
{
MsgGlobal.flags = 0;
XplSafeWrite(MsgGlobal.useCount, 0);
MsgGlobal.groupID = XplGetThreadGroupID();
MsgGlobal.directoryHandle = NULL;
MsgGlobal.connManager = 0x0100007F;
MsgGlobal.official.domain[0] = '\0';
MsgGlobal.official.domainLength = 0;
XplOpenLocalSemaphore(MsgGlobal.sem.shutdown, 0);
XplMutexInit(MsgGlobal.sem.uid);
MemoryManagerOpen(NULL);
if (!MDBInit()) {
MemoryManagerClose(NULL);
return 0;
}
MsgGlobal.directoryHandle = MsgGetSystemDirectoryHandle();
if (!(MsgGlobal.directoryHandle)) {
MemoryManagerClose(NULL);
return 0;
}
if (!MsgReadConfiguration()) {
XplConsolePrintf("Cannot read configuration. Shutting down.\n");
MemoryManagerClose(NULL);
return(0);
}
MsgLibraryStart();
MsgDateStart();
if (curl_global_init(CURL_GLOBAL_ALL)) {
XplConsolePrintf("Could not initialize curl.\r\n");
return 0;
}
MSGAPIState = LIBRARY_RUNNING;
return 1;
}
static void
MsgLibraryShutdown(void)
{
XplThreadID oldGid;
if (MSGAPIState == LIBRARY_RUNNING) {
MSGAPIState = LIBRARY_SHUTTING_DOWN;
MsgGlobal.flags |= MSGAPI_FLAG_EXITING;
oldGid = XplSetThreadGroupID(MsgGlobal.groupID);
MsgLibraryStop();
MemoryManagerClose(NULL);
MSGAPIState = LIBRARY_SHUTDOWN;
XplSetThreadGroupID(oldGid);
}
return;
}
MDBHandle
MsgDirectoryHandle(void)
{
return(MsgGlobal.directoryHandle);
}
BOOL
MsgExiting(void)
{
if (MsgGlobal.flags & MSGAPI_FLAG_EXITING) {
return(TRUE);
}
return(FALSE);
}
EXPORT BOOL
MsgShutdown(void)
{
XplSafeDecrement(MsgGlobal.useCount);
if (XplSafeRead(MsgGlobal.useCount) > 0) {
return(FALSE);
}
MsgLibraryShutdown();
return(TRUE);
}
EXPORT MDBHandle
MsgInit(void)
{
if (MSGAPIState == LIBRARY_LOADED) {
MSGAPIState = LIBRARY_INITIALIZING;
if (!MsgLibraryInit()) {
return NULL;
}
}
while (MSGAPIState < LIBRARY_RUNNING) {
XplDelay(100);
}
if (MSGAPIState == LIBRARY_RUNNING) {
XplSafeIncrement(MsgGlobal.useCount);
}
return((MSGAPIState == LIBRARY_RUNNING) ? MsgGlobal.directoryHandle : NULL);
}
EXPORT BOOL
MsgSetRecoveryFlag(void)
{
// FIXME: TODO :)
return FALSE;
}
EXPORT BOOL
MsgGetRecoveryFlag(void)
{
// FIXME: TODO :)
return FALSE;
}
EXPORT BOOL
MsgGetServerCredential(char *buffer)
{
MDBValueStruct *v;
if (MsgGlobal.directoryHandle) {
v = MDBCreateValueStruct(MsgGlobal.directoryHandle, NULL);
if (v) {
MDBRead(MSGSRV_ROOT, MSGSRV_A_ACL, v);
if (v->Used) {
HashCredential(MsgGlobal.server.dn, v->Value[0], buffer);
}
MDBDestroyValueStruct(v);
return TRUE;
}
}
return FALSE;
}