This will allow us to auto-purge stale records later.
$Log$
+Revision 655.21 2005/10/06 03:36:05 ajc
+* Changed the format of the euidindex record to contain the record's key.
+ This will allow us to auto-purge stale records later.
+
Revision 655.20 2005/10/04 16:38:17 ajc
* CtdlOutputPreLoadedMsg() calling syntax has changed. It no longer needs
the message number, because it is being supplied a preloaded message.
Fri Jul 10 1998 Art Cancro <ajc@uncensored.citadel.org>
* Initial CVS import
+
/*
* Text description of this software
*/
-#define CITADEL "Citadel 6.57"
+#define CITADEL "Citadel 6.58"
/*
* REV_LEVEL is the current version number (multiplied by 100 to avoid having
* usually more strict because you're not really supposed to dump/load and
* upgrade at the same time.
*/
-#define REV_LEVEL 657 /* This version */
+#define REV_LEVEL 658 /* This version */
#define REV_MIN 591 /* Oldest compatible database */
#define EXPORT_REV_MIN 655 /* Oldest compatible export files */
#include "tools.h"
#include "euidindex.h"
+/*
+ * The structure of an EUID *index* is:
+ *
+ * |----room_number----|----------EUID-------------|
+ * (sizeof long) (actual length of euid)
+ *
+ *
+ * The structure of an EUID *record* is:
+ *
+ * |-----msg_number----|----room_number----|----------EUID-------------|
+ * (sizeof long) (sizeof long) (actual length of euid)
+ *
+ */
long locate_message_by_euid(char *euid, struct ctdlroom *qrbuf) {
char *key;
free(key);
if (cdb_euid == NULL) {
- return(-1L);
+ msgnum = (-1L);
}
-
- if (cdb_euid->len == sizeof(long)) {
- msgnum = *(long *)cdb_euid->ptr;
+ else {
+ /* The first (sizeof long) of the record is what we're
+ * looking for. Throw away the rest.
+ */
+ memcpy(&msgnum, cdb_euid->ptr, sizeof(long));
+ cdb_free(cdb_euid);
}
-
- cdb_free(cdb_euid);
- lprintf(CTDL_DEBUG, "returning msgnum==%ld\n", msgnum);
+ lprintf(CTDL_DEBUG, "returning msgnum = %ld\n", msgnum);
return(msgnum);
}
void index_message_by_euid(char *euid, struct ctdlroom *qrbuf, long msgnum) {
char *key;
int key_len;
+ char *data;
+ int data_len;
lprintf(CTDL_DEBUG, "Indexing message #%ld <%s> in <%s>\n", msgnum, euid, qrbuf->QRname);
memcpy(key, &qrbuf->QRnumber, sizeof(long));
strcpy(&key[sizeof(long)], euid);
- cdb_store(CDB_EUIDINDEX, key, key_len, &msgnum, sizeof(long));
+ data_len = sizeof(long) + key_len;
+ data = malloc(data_len);
+
+ memcpy(data, &msgnum, sizeof(long));
+ memcpy(&data[sizeof(long)], key, key_len);
+
+ cdb_store(CDB_EUIDINDEX, key, key_len, data, data_len);
free(key);
+ free(data);
}
* Globally rebuild the EUID indices in every room.
*/
void rebuild_euid_index(void) {
- cdb_trunc(CDB_EUIDINDEX);
-
- ForEachRoom(rebuild_euid_index_for_room, NULL);
- rebuild_euid_index_for_room(NULL, NULL);
+ cdb_trunc(CDB_EUIDINDEX); /* delete the old indices */
+ ForEachRoom(rebuild_euid_index_for_room, NULL); /* enumerate the room names */
+ rebuild_euid_index_for_room(NULL, NULL); /* now do indexing on them */
}
/* Free up the memory we used. */
free(msglist);
+ /* If the message has an Exclusive ID, index that... */
+ if (msg != NULL) {
+ if (msg->cm_fields['E'] != NULL) {
+ index_message_by_euid(msg->cm_fields['E'], &CC->room, msgid);
+ }
+ }
+
/* Update the highest-message pointer and unlock the room. */
CC->room.QRhighest = highest_msg;
lputroom(&CC->room);
/* Bump the reference count for this message. */
AdjRefCount(msgid, +1);
- /* If the message has an Exclusive ID, index that... */
- if (msg != NULL) {
- if (msg->cm_fields['E'] != NULL) {
- index_message_by_euid(msg->cm_fields['E'], &CC->room, msgid);
- }
- }
-
/* Return success. */
if ( (msg != NULL) && (msg != supplied_msg) ) CtdlFreeMessage(msg);
return (0);
if (msg == NULL) return;
if (msg->cm_fields['E'] == NULL) return;
if (strlen(msg->cm_fields['E']) == 0) return;
- lprintf(CTDL_DEBUG, "Exclusive ID: <%s>\n", msg->cm_fields['E']);
+ lprintf(CTDL_DEBUG, "Exclusive ID: <%s> for room <%s>\n",
+ msg->cm_fields['E'], CC->room.QRname);
old_msgnum = locate_message_by_euid(msg->cm_fields['E'], &CC->room);
if (old_msgnum > 0L) {
if ((CitControl.version > 000) && (CitControl.version < 608)) {
convert_ctdluid_to_minusone();
}
- if ((CitControl.version > 000) && (CitControl.version < 657)) {
+ if ((CitControl.version > 000) && (CitControl.version < 658)) {
rebuild_euid_index();
}
code, but this'll get you started.
- Database tables
-
+ DATABASE TABLES
+ ---------------
As you probably already know by now, Citadel uses a group of tables stored
with a record manager (usually Berkeley DB). Since we're using a record
field.
+ EUID (EXCLUSIVE MESSAGE ID'S)
+ -----------------------------
+
+ This is where the groupware magic happens. Any message in any room may have
+a field called the Exclusive message ID, or EUID. We keep an index in the
+table CDB_EUIDINDEX which knows the message number of any item that has an
+EUID. This allows us to do two things:
+
+ 1. If a subsequent message arrives with the same EUID, it automatically
+*deletes* the existing one, because the new one is considered a replacement
+for the existing one.
+ 2. If we know the EUID of the item we're looking for, we can fetch it by EUID
+and get the most up-to-date version, even if it's been updated several times.
+ This functionality is made more useful by server-side hooks. For example,
+when we save a vCard to an address book room, or an iCalendar item to a
+calendar room, our server modules detect this condition, and automatically set
+the EUID of the message to the UUID of the vCard or iCalendar item. Therefore
+when you save an updated version of an address book entry or a calendar item,
+the old one is automatically deleted.
- Networking
+
+ NETWORKING (REPLICATION)
+ ------------------------
Citadel nodes network by sharing one or more rooms. Any Citadel node
can choose to share messages with any other Citadel node, through the sending
of spool files. The sending system takes all messages it hasn't sent yet, and
spools them to the recieving system, which posts them in the rooms.
+The EUID discussion above is extremely relevant, because EUID is carried over
+the network as well, and the replacement rules are followed over the network
+as well. Therefore, when a message containing an EUID is saved in a networked
+room, it replaces any existing message with the same EUID *on every node in
+the network*.
+
Complexities arise primarily from the possibility of densely connected
networks: one does not wish to accumulate multiple copies of a given
message, which can easily happen. Nor does one want to see old messages
With the path present, all the networker has to do to assure that it doesn't
send another system a message it's already received is check the <P>ath field
for that system's name somewhere in the bang path. If it's present, the system
-has already seen the message, so we don't send it. (Note that the current
-implementation does not allow for "loops" in the network -- if you build your
-net this way you will see lots of duplicate messages.)
+has already seen the message, so we don't send it.
+
+We also keep a small database, called the "use table," containing the ID's of
+all messages we've seen recently. If the same message arrives a second or
+subsequent time, we will find its ID in the use table, indicating that we
+already have a copy of that message. It will therefore be discarded.
The above discussion should make the function of the fields reasonably clear:
please see network.txt on its operation and functionality (if any).
-
- Portability issues
+ PORTABILITY ISSUES
+ ------------------
Citadel is 64-bit clean, architecture-independent, and Year 2000
compliant. The software should compile on any POSIX compliant system with
-
-
- SUPPORTING PRIVATE MAIL
+ SUPPORTING PRIVATE MAIL
+ -----------------------
Can one have an elegant kludge? This must come pretty close.
- PASSWORDS AND NAME VALIDATION
+ PASSWORDS AND NAME VALIDATION
+ -----------------------------
This has changed a couple of times over the course of Citadel's history. At
this point it's very simple, again due to the fact that record managers are