/*
* ratDSN.c --
*
* This file handles the delivery status notifications.
*
* TkRat software and its included text is Copyright 1996-2002 by
* Martin Forssén
*
* The full text of the legal notice is contained in the file called
* COPYRIGHT, included with this distribution.
*/
#include "ratFolder.h"
#include <unistd.h>
typedef struct {
char *envid; /* Original envelope ID */
Tcl_Obj *msgFields; /* Contains tcl list of msg fields */
int numRecipients; /* Number of recipients mentioned in this DSN*/
char **actionPtrPtr; /* The action */
char **recTypePtrPtr; /* Type of recipient addresses */
char **recipientPtrPtr; /* Recipient addresses */
Tcl_Obj **rListPtrPtr; /* Recipient fields */
} RatDeliveryStatus;
/*
* Static data
*/
static Tcl_HashTable seenTable;
/*
* Local functions
*/
static Tcl_Channel OpenIndex(Tcl_Interp *interp, char *mode);
static Tcl_ObjCmdProc RatDSNList;
static Tcl_ObjCmdProc RatDSNGet;
static int RatDSNExpire(Tcl_Interp *interp, Tcl_Obj *lineObj);
static char *RatParseDSNLine(char *buf, Tcl_Obj **name, Tcl_Obj **value,
int *length);
static RatDeliveryStatus *RatParseDS(Tcl_Interp *interp, Tcl_Obj *body);
static void RatFreeDeliveryStatus(RatDeliveryStatus *statusPtr);
/*
*----------------------------------------------------------------------
*
* RatDSNInit --
*
* Initializes the DSN system. That is adds the apropriate commands
* to the interpreter.
*
* Results:
* A standard tcl resutl.
*
* Side effects:
* Commands are created in the interpreter.
*
*
*----------------------------------------------------------------------
*/
int
RatDSNInit(Tcl_Interp *interp)
{
Tcl_InitHashTable(&seenTable, TCL_STRING_KEYS);
Tcl_CreateObjCommand(interp, "RatDSNList", RatDSNList, NULL, NULL);
Tcl_CreateObjCommand(interp, "RatDSNGet", RatDSNGet, NULL, NULL);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* RatDSNStartMessage --
*
* Start recording a new DSN message.
*
* Results:
* None.
*
* Side effects:
* A new current message is initialized.
*
*
*----------------------------------------------------------------------
*/
DSNhandle
RatDSNStartMessage(Tcl_Interp *interp, const char *id, const char *subject)
{
Tcl_DString *dsPtr = (Tcl_DString*)ckalloc(sizeof(Tcl_DString));
unsigned char buf[32], *header, *cPtr;
time_t seconds;
Tcl_DStringInit(dsPtr);
Tcl_DStringAppendElement(dsPtr, id);
seconds = time(NULL);
sprintf(buf, "%d", (int)seconds);
Tcl_DStringAppendElement(dsPtr, buf);
header = RatDecodeHeader(interp, subject, 0);
for (cPtr = header; *cPtr; cPtr++) {
if (*cPtr < 32) {
*cPtr = ' ';
}
}
Tcl_DStringAppendElement(dsPtr, header);
Tcl_DStringStartSublist(dsPtr);
return (DSNhandle)dsPtr;
}
/*
*----------------------------------------------------------------------
*
* RatDSNAddRecipient --
*
* Add a recipient to the currently recording DSN message.
*
* Results:
* None.
*
* Side effects:
* The current message is modified.
*
*
*----------------------------------------------------------------------
*/
void
RatDSNAddRecipient(Tcl_Interp *interp, DSNhandle handle, char *recipient)
{
Tcl_DString *dsPtr = (Tcl_DString*)handle;
Tcl_DStringStartSublist(dsPtr);
Tcl_DStringAppendElement(dsPtr, "none");
Tcl_DStringAppendElement(dsPtr, recipient);
Tcl_DStringAppendElement(dsPtr, "");
Tcl_DStringEndSublist(dsPtr);
}
/*
*----------------------------------------------------------------------
*
* RatDSNAbort --
*
* Aborts the composition of the indicated DSN message.
*
* Results:
* None.
*
* Side effects:
* The handle becomes invalid.
*
*
*----------------------------------------------------------------------
*/
void
RatDSNAbort(Tcl_Interp *interp, DSNhandle handle)
{
Tcl_DString *dsPtr = (Tcl_DString*)handle;
if (dsPtr) {
Tcl_DStringFree(dsPtr);
ckfree(dsPtr);
}
}
/*
*----------------------------------------------------------------------
*
* RatDSNFinish --
*
* Moves the message under construction to the list of outstanding
* DSN's.
*
* Results:
* None.
*
* Side effects:
* The current message is cleared.
*
*
*----------------------------------------------------------------------
*/
void
RatDSNFinish(Tcl_Interp *interp, DSNhandle handle)
{
Tcl_Channel channel = OpenIndex(interp, "a");
Tcl_DString *dsPtr = (Tcl_DString*)handle;
if (!channel) {
Tcl_BackgroundError(interp);
return;
}
Tcl_DStringEndSublist(dsPtr);
Tcl_Write(channel, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr));
Tcl_Write(channel, "\n", 1);
Tcl_Close(interp, channel);
Tcl_DStringFree(dsPtr);
ckfree(dsPtr);
}
/*
*----------------------------------------------------------------------
*
* RatDSNHandle --
*
* Handle an incoming DSN.
*
* Results:
* Returns true if the given DSN matched one of those in our list.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
int
RatDSNHandle (Tcl_Interp *interp, char *msg)
{
int i, j, new, match = 0, changed = 0, objc, rfound, perm, robjc, srobjc;
RatDeliveryStatus *statusPtr;
Tcl_HashEntry *entryPtr;
Tcl_Channel channel;
char buf[1024], id[1024], *oldId, *msgFile = NULL, *file;
const char *dir;
Tcl_CmdInfo cmdInfo;
Tcl_Obj *oPtr, *linePtr, *l1Ptr, *l2Ptr, *l3Ptr, *l4Ptr, **objv, **robjv,
**srobjv;
/*
* Avoid processing the same DSN twice
*/
entryPtr = Tcl_CreateHashEntry(&seenTable, msg, &new);
if (!new) {
return (int)Tcl_GetHashValue(entryPtr);
}
Tcl_SetHashValue(entryPtr, 0);
snprintf(buf, sizeof(buf), "[lindex [[%s body] children] 1] data 0", msg);
if (TCL_OK != Tcl_Eval(interp, buf)) {
return 0;
}
statusPtr = RatParseDS(interp, Tcl_GetObjResult(interp));
if (!statusPtr->envid) {
RatFreeDeliveryStatus(statusPtr);
return 0;
}
if (NULL == (channel = OpenIndex(interp, "r"))) {
RatFreeDeliveryStatus(statusPtr);
return 0;
}
l1Ptr = Tcl_NewObj();
oPtr = Tcl_GetVar2Ex(interp, "option", "permissions", TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &perm);
dir = RatGetPathOption(interp, "dsn_directory");
while (linePtr = Tcl_NewObj(), -1 != Tcl_GetsObj(channel, linePtr)) {
/* Join lines until we have a valid list */
while (0 != Tcl_ListObjLength(interp, linePtr, &i)
&& -1 != Tcl_GetsObj(channel, linePtr));
if (i != 4) {
/* If the list does not have 4 elements it is invalid */
continue;
}
if (RatDSNExpire(interp, linePtr)) {
/*
* This DSN has expired so we should remove all associated files
*/
Tcl_ListObjIndex(interp, linePtr, 0, &oPtr);
snprintf(buf, sizeof(buf), "%s/%s", dir, Tcl_GetString(oPtr));
(void)unlink(buf);
Tcl_ListObjLength(interp, linePtr, &i);
Tcl_ListObjIndex(interp, linePtr, i-1, &oPtr);
Tcl_ListObjGetElements(interp, oPtr, &objc, &objv);
for (i=0; i < objc; i++) {
Tcl_ListObjIndex(interp, objv[i], 2, &oPtr);
file = Tcl_GetString(oPtr);;
if (strlen(file)) {
snprintf(buf, sizeof(buf), "%s/%s", dir, file);
(void)unlink(buf);
}
}
changed++;
continue;
}
Tcl_ListObjIndex(interp, linePtr, 0, &oPtr);
if (strcmp(Tcl_GetString(oPtr), statusPtr->envid)) {
Tcl_ListObjAppendElement(interp, l1Ptr, linePtr);
continue;
}
changed++;
match = 1;
l2Ptr = Tcl_NewObj();
for (i=0; i<3; i++) {
Tcl_ListObjIndex(interp, linePtr, i, &oPtr);
Tcl_ListObjAppendElement(interp, l2Ptr, oPtr);
}
l3Ptr = Tcl_NewObj();
Tcl_ListObjLength(interp, linePtr, &i);
Tcl_ListObjIndex(interp, linePtr, i-1, &oPtr);
Tcl_ListObjGetElements(interp, oPtr, &robjc, &robjv);
for (i=0; i<robjc; i++) {
for (j=rfound=0; !rfound && j<statusPtr->numRecipients; j++) {
Tcl_ListObjGetElements(interp, robjv[i], &srobjc, &srobjv);
if (statusPtr->recTypePtrPtr[j]
&& statusPtr->actionPtrPtr[j]
&& !strcasecmp(statusPtr->recTypePtrPtr[j], "rfc822")
&& !strcmp(statusPtr->recipientPtrPtr[j],
Tcl_GetString(srobjv[1]))
&& strcmp(statusPtr->actionPtrPtr[j],
Tcl_GetString(srobjv[0]))) {
/*
* This DSN matched this recipient
* We start by saving the DSN message;
* then we add it to the index file.
* Finally we notify the user.
*/
rfound = 1;
oldId = Tcl_GetString(srobjv[2]);
RatGenId(NULL, interp, 0, NULL);
strlcpy(id, Tcl_GetStringResult(interp), sizeof(id));
if (strlen(oldId)) {
snprintf(buf, sizeof(buf), "%s/%s", dir, oldId);
(void)unlink(buf);
}
snprintf(buf, sizeof(buf), "%s/%s", dir, id);
if (!msgFile) {
Tcl_DString msgDS;
Tcl_Channel msgCh;
msgFile = cpystr(buf);
Tcl_DStringInit(&msgDS);
Tcl_GetCommandInfo(interp, msg, &cmdInfo);
RatMessageGet(interp,
(MessageInfo*)cmdInfo.objClientData,
&msgDS, NULL, 0, NULL, 0);
msgCh = Tcl_OpenFileChannel(interp, msgFile, "w",perm);
Tcl_Write(msgCh, Tcl_DStringValue(&msgDS),
Tcl_DStringLength(&msgDS));
Tcl_Close(interp, msgCh);
Tcl_DStringFree(&msgDS);
} else {
link(msgFile, buf);
}
l4Ptr = Tcl_NewObj();
oPtr = Tcl_NewStringObj(statusPtr->actionPtrPtr[j], -1);
Tcl_ListObjAppendElement(interp, l4Ptr, oPtr);
oPtr = Tcl_NewStringObj(statusPtr->recipientPtrPtr[j], -1);
Tcl_ListObjAppendElement(interp, l4Ptr, oPtr);
oPtr = Tcl_NewStringObj(id, -1);
Tcl_ListObjAppendElement(interp, l4Ptr, oPtr);
Tcl_ListObjAppendElement(interp, l3Ptr, l4Ptr);
Tcl_ListObjIndex(interp, linePtr, 2, &oPtr);
Tcl_VarEval(interp, "RatDSNRecieve {",
Tcl_GetString(oPtr), "} {",
statusPtr->actionPtrPtr[j], "} {",
statusPtr->recipientPtrPtr[j], "} {", id,
"}",NULL);
}
}
if (!rfound) {
Tcl_ListObjAppendElement(interp, l3Ptr, robjv[i]);
}
}
Tcl_ListObjAppendElement(interp, l2Ptr, l3Ptr);
Tcl_ListObjAppendElement(interp, l1Ptr, l2Ptr);
}
Tcl_Close(interp, channel);
RatFreeDeliveryStatus(statusPtr);
if (changed) {
if (NULL == (channel = OpenIndex(interp, "w"))) {
return 0;
}
Tcl_ListObjGetElements(interp, l1Ptr, &objc, &objv);
for (i=0; i<objc; i++) {
Tcl_WriteObj(channel, objv[i]);
Tcl_Write(channel, "\n", 1);
}
Tcl_Close(interp, channel);
}
Tcl_DecrRefCount(l1Ptr);
Tcl_DecrRefCount(linePtr);
Tcl_SetHashValue(entryPtr, match);
ckfree(msgFile);
return match;
}
/*
*----------------------------------------------------------------------
*
* RatDSNExtract --
*
* Extract the DSN data from a dsn body part
*
* Results:
* A standard tcl result and the requested data in the result area
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
int
RatDSNExtract (Tcl_Interp *interp, Tcl_Obj *body)
{
RatDeliveryStatus *sPtr = RatParseDS(interp, body);
Tcl_Obj *rPtr, *oPtr;
int i;
rPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, rPtr, sPtr->msgFields);
oPtr = Tcl_NewObj();
for (i=0; i<sPtr->numRecipients; i++) {
Tcl_ListObjAppendElement(interp, oPtr, sPtr->rListPtrPtr[i]);
}
Tcl_ListObjAppendElement(interp, rPtr, oPtr);
Tcl_SetObjResult(interp, rPtr);
RatFreeDeliveryStatus(sPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* OpenIndex --
*
* Opens the DSN indexfile.
*
* Results:
* A Tcl channel handle. If an error occurs NULL is returned
* and an error message is left in the result area.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static Tcl_Channel
OpenIndex(Tcl_Interp *interp, char *mode)
{
char buf[1024];
const char *dir;
struct stat sbuf;
Tcl_Channel channel;
int perm;
Tcl_Obj *oPtr;
oPtr = Tcl_GetVar2Ex(interp, "option", "permissions", TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &perm);
dir = RatGetPathOption(interp, "dsn_directory");
if (stat(dir, &sbuf)) {
if (mkdir(dir, perm|0100)) {
Tcl_AppendResult(interp, "Failed to create directory \"",
dir, "\" :", Tcl_PosixError(interp), NULL);
return NULL;
}
} else if (!S_ISDIR(sbuf.st_mode)) {
Tcl_AppendResult(interp, "This is no directory \"", dir, "\"", NULL);
return NULL;
}
snprintf(buf, sizeof(buf), "%s/index", dir);
if (NULL == (channel = Tcl_OpenFileChannel(interp, buf, mode, perm))) {
return NULL;
}
Tcl_SetChannelOption(interp, channel, "-encoding", "utf-8");
return channel;
}
/*
*----------------------------------------------------------------------
*
* RatDSNList --
*
* List the currently known DSN's
*
* Results:
* See ../doc/interface
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static int
RatDSNList (ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[])
{
Tcl_Channel channel = OpenIndex(interp, "r");
Tcl_Obj *oPtr, *rPtr;
if (!channel) {
Tcl_ResetResult(interp);
return TCL_OK;
}
rPtr = Tcl_NewObj();
while (oPtr = Tcl_NewObj(), -1 != Tcl_GetsObj(channel, oPtr)) {
if (!RatDSNExpire(interp, oPtr)) {
Tcl_ListObjAppendElement(interp, rPtr, oPtr);
} else {
Tcl_DecrRefCount(oPtr);
}
}
Tcl_DecrRefCount(oPtr);
if (!Tcl_Eof(channel)) {
Tcl_Close(interp, channel);
Tcl_DecrRefCount(rPtr);
return TCL_ERROR;
}
Tcl_Close(interp, channel);
Tcl_SetObjResult(interp, rPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* RatDSNGet --
*
* Get information about a DSN.
*
* Results:
* See ../doc/interface
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static int
RatDSNGet (ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[])
{
char buf[1024], *msg, *data;
const char *dir;
RatDeliveryStatus *statusPtr;
Tcl_Channel channel;
int i, len;
Tcl_Obj *rPtr, *oPtr;
if (objc != 3 && objc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetString(objv[0]), " what id ?recipient?\"",
(char *) NULL);
return TCL_ERROR;
}
if (strcmp(Tcl_GetString(objv[1]), "msg")
&& strcmp(Tcl_GetString(objv[1]), "report")) {
Tcl_AppendResult(interp, "Illegal 'what' argument; should be ",
"'msg' or 'report'.", (char*) NULL);
return TCL_ERROR;
}
if (!strlen(Tcl_GetString(objv[2]))) {
Tcl_SetResult(interp, "Empty 'id' argument.", TCL_STATIC);
return TCL_ERROR;
}
dir = RatGetPathOption(interp, "dsn_directory");
snprintf(buf, sizeof(buf), "%s/%s", dir, Tcl_GetString(objv[2]));
if (NULL == (channel = Tcl_OpenFileChannel(interp, buf, "r", 0))) {
return TCL_ERROR;
}
len = Tcl_Seek(channel, 0, SEEK_END);
data = (char*) ckalloc(len+1);
Tcl_Seek(channel, 0, SEEK_SET);
len = Tcl_Read(channel, data, len);
data[len] = '\0';
Tcl_Close(interp, channel);
msg = RatFrMessageCreate(interp, data, len, NULL);
ckfree(data);
if (!strcmp(Tcl_GetString(objv[1]), "msg")) {
Tcl_SetResult(interp, msg, TCL_VOLATILE);
} else {
snprintf(buf,sizeof(buf),"[lindex [[%s body] children] 1] data 0",msg);
if (TCL_OK != Tcl_Eval(interp, buf)) {
return TCL_ERROR;
}
statusPtr = RatParseDS(interp, Tcl_GetObjResult(interp));
rPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, rPtr, statusPtr->msgFields);
oPtr = Tcl_NewObj();
for (i=0; i < statusPtr->numRecipients; i++) {
if (!strcmp(statusPtr->recipientPtrPtr[i],
Tcl_GetString(objv[3]))){
Tcl_ListObjAppendElement(interp, oPtr,
Tcl_NewStringObj(statusPtr->recipientPtrPtr[i], -1));
break;
}
}
Tcl_ListObjAppendElement(interp, rPtr, oPtr);
Tcl_SetObjResult(interp, rPtr);
RatFreeDeliveryStatus(statusPtr);
RatMessageDelete(interp, msg);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* RatDSNExpire --
*
* Check if a given DSN line has expired.
*
* Results:
* Returns true if it has expired.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static int
RatDSNExpire(Tcl_Interp *interp, Tcl_Obj *linePtr)
{
Tcl_Obj *oPtr;
long intime;
int days;
oPtr = Tcl_GetVar2Ex(interp, "option", "dsn_expiration", TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &days);
Tcl_ListObjIndex(interp, linePtr, 1, &oPtr);
Tcl_GetLongFromObj(interp, oPtr, &intime);
return (intime+days*24*60*60 < time(NULL));
}
/*
*----------------------------------------------------------------------
*
* RatParseDSNLine --
*
* Extract the next line of DSN information from a message/delivery-status
* message.
*
* Results:
* The name and value pointers objects are filled in.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static char*
RatParseDSNLine(char *line, Tcl_Obj **name, Tcl_Obj **value, int *length)
{
char *s, *e, *n;
*name = *value = NULL;
/* Find start of name */
for (s=line; ' ' == *s || '\t' == *s; s++);
/* Find end of name */
for (e=s; *e && ':' != *e && '\r' != *e && '\n' != *e; e++);
if (':' != *e) {
goto bad;
}
n = e+1;
for (e--; isspace(e[-1]) && e>s; e--);
*name = Tcl_NewStringObj(s, e-s+1);
/* Find start of value */
for (s=n; ' ' == *s || '\t' == *s; s++);
/* Find end of value */
for (e=s; (e=strstr(e, "\r\n")) && (' ' == e[2] || '\t' == e[2]); e++);
if (NULL == e) {
n = e = s+strlen(s);
} else {
n = e+2;
}
*value = Tcl_NewStringObj(s, e-s);
*length -= n-line;
return n;
bad:
if (*name) {
Tcl_DecrRefCount(*name);
*name = NULL;
}
for (e=line; (e=strstr(e, "\r\n")) && (' '==e[2] || '\t'==e[2]); e++);
if (e) {
*length -= e-line;
return e;
} else {
*length -= strlen(line);
return NULL;
}
}
/*
*----------------------------------------------------------------------
*
* RatParseDS --
*
* Parse a message/delivery-status body.
*
* Results:
* A pointer to a RatDeliveryStatus structure. It is the callers
* responsibility to free this pointer later with a call to
* RatFreeDeliveryStatus().
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static RatDeliveryStatus*
RatParseDS(Tcl_Interp *interp, Tcl_Obj *bPtr)
{
RatDeliveryStatus *sPtr = (RatDeliveryStatus*)ckalloc(sizeof(*sPtr));
char *cPtr, *body;
int allocated, i, length;
Tcl_Obj *ov[2];
body = Tcl_GetStringFromObj(bPtr, &length);
/*
* Parse the per message fields.
*/
sPtr->envid = NULL;
while (strchr(" \t\015\012", *body)) {
body++;
length--;
}
sPtr->msgFields = Tcl_NewObj();
Tcl_IncrRefCount(sPtr->msgFields);
while (length > 0 && body) {
body = RatParseDSNLine(body, &ov[0], &ov[1], &length);
if (!ov[0]) {
break;
}
Tcl_ListObjAppendElement(interp, sPtr->msgFields,
Tcl_NewListObj(2, ov));
if (!strcasecmp("original-envelope-id", Tcl_GetString(ov[0]))) {
sPtr->envid = cpystr(Tcl_GetString(ov[1]));
}
/* Added by Lou Ruppert to account for odd DSN implementations */
if (!strcasecmp("arrival-date", Tcl_GetString(ov[0]))
&& '\n' != *body
&& '\r' != *body) {
break;
}
}
/*
* Parse the per recipient fields
*/
sPtr->numRecipients = 0;
sPtr->actionPtrPtr = NULL;
sPtr->recTypePtrPtr = NULL;
sPtr->recipientPtrPtr = NULL;
sPtr->rListPtrPtr = NULL;
allocated = 0;
while (length > 0 && body) {
while (isspace(*body) && length > 0) {
body++;
length--;
}
if (!*body) {
break;
}
if (allocated <= sPtr->numRecipients) {
allocated += 32;
sPtr->actionPtrPtr = (char**)ckrealloc(sPtr->actionPtrPtr,
allocated*sizeof(char*));
sPtr->recTypePtrPtr = (char**)ckrealloc(sPtr->recTypePtrPtr,
allocated*sizeof(char*));
sPtr->recipientPtrPtr = (char**)ckrealloc(sPtr->recipientPtrPtr,
allocated*sizeof(char*));
sPtr->rListPtrPtr = (Tcl_Obj**)ckrealloc(sPtr->rListPtrPtr,
allocated*sizeof(Tcl_DString));
}
i = sPtr->numRecipients++;
sPtr->actionPtrPtr[i] = NULL;
sPtr->recTypePtrPtr[i] = NULL;
sPtr->recipientPtrPtr[i] = NULL;
sPtr->rListPtrPtr[i] = Tcl_NewObj();
Tcl_IncrRefCount(sPtr->rListPtrPtr[i]);
while (length > 0) {
body = RatParseDSNLine(body, &ov[0], &ov[1], &length);
if (!ov[0]) {
break;
}
Tcl_ListObjAppendElement(interp, sPtr->rListPtrPtr[i],
Tcl_NewListObj(2, ov));
if (!strcasecmp("original-recipient", Tcl_GetString(ov[0]))) {
sPtr->recTypePtrPtr[i] = cpystr(Tcl_GetString(ov[1]));
if (NULL != (cPtr = strchr(sPtr->recTypePtrPtr[i], ';'))) {
*cPtr++ = '\0';
}
sPtr->recipientPtrPtr[i] = cPtr;
}
if (!strcasecmp("action", Tcl_GetString(ov[0]))) {
sPtr->actionPtrPtr[i] = cpystr(Tcl_GetString(ov[1]));
}
}
if (!sPtr->actionPtrPtr[i]) {
sPtr->numRecipients--;
sPtr->recTypePtrPtr[i] = NULL;
sPtr->recipientPtrPtr[i] = NULL;
}
}
return sPtr;
}
/*
*----------------------------------------------------------------------
*
* RatFreeDeliveryStatus --
*
* Free a RatDeliveryStatus structure.
*
* Results:
* A pointer to a RatDeliveryStatus structure. It is the callers
* responsibility to free this pointer later with a call to
* RatFreeDeliveryStatus().
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static void
RatFreeDeliveryStatus(RatDeliveryStatus *statusPtr)
{
int i;
ckfree(statusPtr->envid);
Tcl_DecrRefCount(statusPtr->msgFields);
if (statusPtr->numRecipients) {
for (i=0; i<statusPtr->numRecipients; i++) {
ckfree(statusPtr->actionPtrPtr[i]);
ckfree(statusPtr->recTypePtrPtr[i]);
Tcl_DecrRefCount(statusPtr->rListPtrPtr[i]);
}
ckfree(statusPtr->actionPtrPtr);
ckfree(statusPtr->recTypePtrPtr);
ckfree(statusPtr->recipientPtrPtr);
ckfree(statusPtr->rListPtrPtr);
}
ckfree(statusPtr);
}
syntax highlighted by Code2HTML, v. 0.9.1