/* * ratMessage.c -- * * This file contains code which implements the message entities. * * 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 #include #include "ratStdFolder.h" #include "ratPGP.h" #include "osdep.h" /* * An array of commands. It contains one entry for each internal message * type (as defined by RatMessageType). */ static MessageProcInfo *messageProcInfo = NULL; /* * The number of replies created. This is used to create new unique * message handlers. */ static int numReplies = 0; /* * The number of message entities created. This is used to create new * unique command names. */ static int numBodies = 0; static void RatBodyDelete(Tcl_Interp *interp, BodyInfo *bodyInfoPtr); static BodyInfo *RatFindFirstText(BodyInfo *bodyInfoPtr); static CONST84 char *RatGetCitation(Tcl_Interp *interp, MessageInfo *msgPtr); static void RatCiteMessage(Tcl_Interp *interp, Tcl_Obj *dstObjPtr, CONST84 char *src, CONST84 char *myCitation); extern long unix_create (MAILSTREAM *stream,char *mailbox); /* *---------------------------------------------------------------------- * * RatInitMessages -- * * Initialize the message data structures. * * Results: * None. * * Side effects: * The messageProcInfo array is allocated and initialized. * * *---------------------------------------------------------------------- */ void RatInitMessages() { messageProcInfo = (MessageProcInfo*)ckalloc(3*sizeof(MessageProcInfo)); RatStdMessagesInit(&messageProcInfo[RAT_CCLIENT_MESSAGE]); RatDbMessagesInit(&messageProcInfo[RAT_DBASE_MESSAGE]); RatFrMessagesInit(&messageProcInfo[RAT_FREE_MESSAGE]); } /* *---------------------------------------------------------------------- * * RatMessageCmd -- * * Main std mail entity procedure. This routine implements the mail * commands mentioned in ../INTERFACE. * * Results: * A standard tcl result. * * Side effects: * many. * * *---------------------------------------------------------------------- */ int RatMessageCmd(ClientData clientData,Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { MessageInfo *msgPtr = (MessageInfo*) clientData; Tcl_Obj *rPtr; if (objc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " option ?arg?\"", (char *) NULL); return TCL_ERROR; } if (!strcmp(Tcl_GetString(objv[1]), "headers")) { if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " headers\"", (char *) NULL); return TCL_ERROR; } return RatMessageGetHeader(interp, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr)); } else if (!strcmp(Tcl_GetString(objv[1]), "body")) { if (!msgPtr->bodyInfoPtr) { msgPtr->bodyInfoPtr = (*messageProcInfo[msgPtr->type].createBodyProc)(interp, msgPtr); RatPGPBodyCheck(interp, messageProcInfo, &msgPtr->bodyInfoPtr); Tcl_CreateObjCommand(interp, msgPtr->bodyInfoPtr->cmdName, RatBodyCmd, (ClientData) msgPtr->bodyInfoPtr, NULL); } Tcl_SetResult(interp, msgPtr->bodyInfoPtr->cmdName, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "rawText")) { rPtr = Tcl_NewObj(); Tcl_AppendToObj(rPtr, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp,msgPtr), -1); Tcl_AppendToObj(rPtr, "\r\n", 2); Tcl_AppendToObj(rPtr, (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr), -1); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { ENVELOPE *env = (*messageProcInfo[msgPtr->type].envelopeProc)(msgPtr); int i; if (objc < 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " get fields\"", (char *) NULL); return TCL_ERROR; } for (i=2; ireturn_path); } else if (!strcasecmp(Tcl_GetString(objv[i]), "from")) { RatInitAddresses(interp, env->from); } else if (!strcasecmp(Tcl_GetString(objv[i]), "sender")) { RatInitAddresses(interp, env->sender); } else if (!strcasecmp(Tcl_GetString(objv[i]), "reply_to")) { RatInitAddresses(interp, env->reply_to); } else if (!strcasecmp(Tcl_GetString(objv[i]), "to")) { RatInitAddresses(interp, env->to); } else if (!strcasecmp(Tcl_GetString(objv[i]), "cc")) { RatInitAddresses(interp, env->cc); } else if (!strcasecmp(Tcl_GetString(objv[i]), "bcc")) { RatInitAddresses(interp, env->bcc); } else { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "bad field \"",Tcl_GetString(objv[i]), "\": must be one of return_path, from, sender, ", "reply_to, to, cc or bcc", (char*)NULL); return TCL_ERROR; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "reply")) { /* * Construct a reply to a message. We should really handle * different character sets here. /MaF */ ENVELOPE *env = (*messageProcInfo[msgPtr->type].envelopeProc)(msgPtr); char handler[32], buf[1024], *cPtr; BodyInfo *bodyInfoPtr; unsigned long bodylength; ADDRESS *adrPtr; char *dataPtr; Tcl_DString ds; Tcl_Obj *oPtr, *vPtr; Tcl_DStringInit(&ds); if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " reply to\"", (char *) NULL); return TCL_ERROR; } sprintf(handler, "reply%d", numReplies++); if (!strcasecmp(Tcl_GetString(objv[2]), "sender")) { /* * We should construct a reply which should go only to one person. * We look for the address in the following fields: * Reply-To:, From:. Sender:, From * As sson as an address is found the search stops. */ if (env->reply_to) { adrPtr = env->reply_to; } else if (env->from) { adrPtr = env->from; } else if (env->sender) { adrPtr = env->sender; } else { adrPtr = env->return_path; } for (;adrPtr; adrPtr = adrPtr->next) { RatAddressTranslate(interp, adrPtr); if (adrPtr->mailbox) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, ", ", -1); } Tcl_DStringAppend(&ds, RatAddressMail(adrPtr), -1); } } Tcl_SetVar2(interp, handler, "to", Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY); } else { /* * We should construct a reply which goes to everybody who has * recieved this message. This is done by first collecting all * addresses found in: Reply-To:, From:, Sender:, To: and Cc:. * Then go though this list and eliminate myself and any * duplicates. Now we use the first element of the list as To: * and the rest as Cc:. */ ADDRESS **recipientPtrPtr =(ADDRESS**)ckalloc(16*sizeof(ADDRESS*)); int numAllocated = 16; int numRecipients = 0; int inList = 0; ADDRESS *to = NULL; int i, j; #define SCANLIST(x) for (adrPtr = (x); adrPtr; adrPtr = adrPtr->next) { \ if (numRecipients == numAllocated) { \ numAllocated += 16; \ recipientPtrPtr = (ADDRESS**)ckrealloc( \ recipientPtrPtr, \ numAllocated*sizeof(ADDRESS*)); \ } \ recipientPtrPtr[numRecipients++] = adrPtr; \ } SCANLIST(env->reply_to); if (NULL == env->reply_to) { SCANLIST(env->from); } SCANLIST(env->to); SCANLIST(env->cc); for (i=0; ihost) { inList = (inList)? 0 : 1; continue; } if (RatAddressIsMe(interp, adrPtr, 1)) { continue; } RatAddressTranslate(interp, adrPtr); for (j=0; jmailbox, to->host); Tcl_SetVar2(interp, handler, "to", buf, TCL_GLOBAL_ONLY); } ckfree(recipientPtrPtr); } if (env->subject) { int match; cPtr = RatDecodeHeader(interp, env->subject, 0); Tcl_DStringSetLength(&ds, 0); Tcl_DStringAppendElement(&ds, "regexp"); Tcl_DStringAppendElement(&ds, "-nocase"); Tcl_DStringAppendElement(&ds, Tcl_GetVar2(interp, "option","re_regexp",TCL_GLOBAL_ONLY)); Tcl_DStringAppendElement(&ds, cPtr); Tcl_DStringAppendElement(&ds, "reply_match"); if (TCL_OK == Tcl_EvalEx(interp, Tcl_DStringValue(&ds), -1, TCL_EVAL_DIRECT) && NULL != (oPtr = Tcl_GetObjResult(interp)) && TCL_OK == Tcl_GetBooleanFromObj(interp, oPtr, &match) && match) { CONST84 char *s = Tcl_GetVar(interp, "reply_match", 0); if (!strncmp(s, cPtr, strlen(s))) { cPtr += strlen(s); while (isspace(*cPtr)) cPtr++; } } oPtr = Tcl_NewStringObj("Re: ", 4); Tcl_AppendToObj(oPtr, cPtr, -1); Tcl_SetVar2Ex(interp, handler,"subject", oPtr,TCL_GLOBAL_ONLY); } else { Tcl_SetVar2(interp, handler, "subject", Tcl_GetVar2(interp, "option","no_subject",TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY); } if (env->message_id) { Tcl_SetVar2(interp, handler, "in_reply_to", env->message_id, TCL_GLOBAL_ONLY); } bodyInfoPtr = RatFindFirstText(msgPtr->bodyInfoPtr); if (bodyInfoPtr && (NULL != (dataPtr = (*messageProcInfo[bodyInfoPtr->msgPtr->type].fetchBodyProc) (bodyInfoPtr, &bodylength)))) { CONST84 char *alias, *attrFormat, *charset = "us-ascii", *citation; ListExpression *exprPtr; PARAMETER *parameter; Tcl_DString *decBPtr; BODY *bodyPtr; int wrap; bodyPtr = bodyInfoPtr->bodyPtr; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { charset = parameter->value; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", (char*)charset, TCL_GLOBAL_ONLY))) { charset = alias; } decBPtr = RatDecode(interp, bodyPtr->encoding, dataPtr, bodylength, charset); attrFormat = Tcl_GetVar2(interp, "option", "attribution", TCL_GLOBAL_ONLY); if (attrFormat && strlen(attrFormat) && (exprPtr = RatParseList(attrFormat))) { oPtr = RatDoList(interp, exprPtr, messageProcInfo[msgPtr->type].getInfoProc, (ClientData)msgPtr, 0); RatFreeListExpression(exprPtr); Tcl_AppendToObj(oPtr, "\n", 1); } else { oPtr = Tcl_NewObj(); } citation = RatGetCitation(interp, msgPtr); RatCiteMessage(interp, oPtr, Tcl_DStringValue(decBPtr), citation); Tcl_DStringFree(decBPtr); vPtr = Tcl_GetVar2Ex(interp,"option","wrap_cited",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, vPtr, &wrap); if (wrap) { Tcl_Obj *nPtr; nPtr = RatWrapMessage(interp, oPtr); Tcl_DecrRefCount(oPtr); oPtr = nPtr; } Tcl_SetVar2Ex(interp, handler, "data", oPtr, TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, handler, "data_tags", "Cited noWrap no_spell", TCL_GLOBAL_ONLY); } Tcl_SetResult(interp, handler, TCL_VOLATILE); Tcl_DStringFree(&ds); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "copy")) { char flags[128], date[128], *spec, *name; Tcl_Obj *defPtr, **dobjv, **eobjv, *oPtr; MAILSTREAM *stream = NULL; struct stat sbuf; Tcl_DString ds, specBuf; STRING string; int dobjc, eobjc, result, i, freeListObjv = 0, specBufUse = 0; RatFolderInfo *infoPtr; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0])," copy vfolder_def\"", NULL); return TCL_ERROR; } defPtr = objv[2]; infoPtr = RatGetOpenFolder(interp, defPtr); if (infoPtr) { name = msgPtr->name; result = RatFolderInsert(interp, infoPtr, 1, &name); RatFolderClose(interp, infoPtr, 0); return result; } Tcl_ListObjGetElements(interp, defPtr, &dobjc, &dobjv); /* * If the destination is dbase then we call RatInsert */ if (!strcmp("dbase", Tcl_GetString(dobjv[1]))) { Tcl_ListObjGetElements(interp, dobjv[5], &eobjc, &eobjv); for (i=0; itype].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_MAIL_REAL, 0); if (oPtr) { name = Tcl_GetString(oPtr); } if (!name) { struct passwd *passwdPtr = getpwuid(getuid()); name = passwdPtr->pw_name; } defPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, defPtr, dobjv[0]); Tcl_IncrRefCount(dobjv[0]); Tcl_ListObjAppendElement(interp, defPtr, Tcl_NewStringObj("file", 4)); Tcl_ListObjAppendElement(interp, defPtr, dobjv[2]); Tcl_IncrRefCount(dobjv[2]); oPtr = Tcl_DuplicateObj(dobjv[3]); Tcl_AppendToObj(oPtr, "/", 1); for (i=0; name[i] && name[i] != '@'; i++); Tcl_AppendToObj(oPtr, name, i); Tcl_ListObjAppendElement(interp, defPtr, oPtr); freeListObjv = 1; Tcl_ListObjGetElements(interp, defPtr, &dobjc, &dobjv); } /* * Try to create nonexisting files */ if (!strcmp("file", Tcl_GetString(dobjv[1]))) { name = RatGetFolderSpec(interp, defPtr); if (!strcmp(name, "INBOX")) { name = sysinbox(); } if (0 != stat(name, &sbuf)) { unix_create(NIL, name); } } /* * Try the case where both source and destination are c-client * messages of sufficiently same type. */ if (RAT_CCLIENT_MESSAGE == msgPtr->type && RatStdEasyCopyingOK(interp, msgPtr, defPtr)) { result = RatStdMessageCopy(interp, msgPtr, RatGetFolderSpec(interp, defPtr)); goto end_copy; } /* * Open a folder and get the stream */ spec = RatGetFolderSpec(interp, defPtr); stream = OpenStdFolder(interp, spec, NULL); if (stream) { Tcl_DStringInit(&ds); RatMessageGet(interp, msgPtr, &ds, flags, sizeof(flags), date, sizeof(date)); INIT(&string, mail_string, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); RatPurgeFlags(flags, 1); if (!mail_append_full(stream, spec, flags, date, &string)){ CloseStdFolder(interp, stream); Tcl_SetResult(interp, "mail_append failed", TCL_STATIC); result = TCL_ERROR; Tcl_DStringFree(&ds); goto end_copy; } if (infoPtr) { RatFolderClose(interp, infoPtr, 0); } else { CloseStdFolder(interp, stream); } Tcl_DStringFree(&ds); result = TCL_OK; } else { result = TCL_ERROR; } end_copy: if (specBufUse) { Tcl_DStringFree(&specBuf); } if (freeListObjv) { Tcl_DecrRefCount(defPtr); } return result; } else if (!strcmp(Tcl_GetString(objv[1]), "list")) { ListExpression *exprPtr; Tcl_Obj *oPtr; if (objc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " list format\"", (char *) NULL); return TCL_ERROR; } if (NULL == (exprPtr = RatParseList(Tcl_GetString(objv[2])))) { Tcl_SetResult(interp, "Illegal list format", TCL_STATIC); return TCL_ERROR; } oPtr = RatDoList(interp, exprPtr, messageProcInfo[msgPtr->type].getInfoProc, (ClientData)msgPtr, 0); Tcl_SetObjResult(interp, oPtr); RatFreeListExpression(exprPtr); return TCL_OK; } else { Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]), "\": must be one of header, body, rawText reply, or get", (char*)NULL); return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatMessageGetHeader -- * * Gets the header of a message * * Results: * The header is returned as a list in the result area. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatMessageGetHeader(Tcl_Interp *interp, char *srcHeader) { char *header, *listArgv[2]; char *dstPtr, *srcPtr = srcHeader, *cPtr, *tPtr; Tcl_Obj *oPtr = Tcl_NewObj(), *fPtr[2]; int adr; if (!srcHeader) { RatLog(interp, RAT_FATAL, Tcl_GetStringResult(interp), RATLOG_TIME); exit(1); } header = (char*) ckalloc (strlen(srcHeader)+1); if (!strncmp("From ", srcPtr, 5)) { while ('\n' != *srcPtr) { srcPtr++; } if ('\r' == *(++srcPtr)) { srcPtr++; } } while (*srcPtr) { dstPtr = header; listArgv[0] = dstPtr = header; while (*srcPtr && ':' != *srcPtr && ' ' != *srcPtr) { *dstPtr++ = *srcPtr++; } *dstPtr = '\0'; fPtr[0] = Tcl_NewStringObj(header, -1); cPtr = ++dstPtr; do { srcPtr++; } while (' ' == *srcPtr || '\t' == *srcPtr); do { for (; *srcPtr && '\n' != *srcPtr; srcPtr++) { if ('\r' != *srcPtr) { *dstPtr++ = *srcPtr; } } while ('\n' == *srcPtr || '\r' == *srcPtr) { srcPtr++; } } while (*srcPtr && (' ' == *srcPtr || '\t' == *srcPtr)); *dstPtr = '\0'; tPtr = cPtr; if (0 == strncasecmp("resent-", tPtr, 7)) { tPtr += 7; } if (!strcasecmp(tPtr, "to") || !strcasecmp(tPtr, "cc") || !strcasecmp(tPtr, "bcc") || !strcasecmp(tPtr, "from") || !strcasecmp(tPtr, "sender") || !strcasecmp(tPtr, "reply-to")) { adr = 1; } else { adr = 0; } fPtr[1] = Tcl_NewStringObj(RatDecodeHeader(interp, cPtr, adr), -1); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewListObj(2, fPtr)); } ckfree(header); Tcl_SetObjResult(interp, oPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatMessageDelete -- * * Deletes the given message. * * Results: * None. * * Side effects: * The message and all its bodyparts are deleted from the interpreter * and all the structures are freed. * * *---------------------------------------------------------------------- */ int RatMessageDelete(Tcl_Interp *interp, char *msgName) { Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr; char buf[256]; int i; if (0 == Tcl_GetCommandInfo(interp, msgName, &cmdInfo)) { Tcl_AppendResult(interp, "No such message: ", msgName, NULL); return TCL_ERROR; } msgPtr = (MessageInfo*)cmdInfo.objClientData; (*messageProcInfo[msgPtr->type].msgDeleteProc)(msgPtr); if (msgPtr->bodyInfoPtr) { if (msgPtr->bodyInfoPtr->altPtr) { RatBodyDelete(interp, msgPtr->bodyInfoPtr->altPtr); } if (msgPtr->bodyInfoPtr->decodedTextPtr) { Tcl_DStringFree(msgPtr->bodyInfoPtr->decodedTextPtr); ckfree(msgPtr->bodyInfoPtr->decodedTextPtr); } if (msgPtr->bodyInfoPtr->secPtr) { RatBodyDelete(interp, msgPtr->bodyInfoPtr->secPtr); } else { RatBodyDelete(interp, msgPtr->bodyInfoPtr); } } snprintf(buf, sizeof(buf), "msgInfo_%s", msgPtr->name); Tcl_UnsetVar(interp, buf, TCL_GLOBAL_ONLY); Tcl_DeleteCommand(interp, msgName); for (i=0; iinfo)/sizeof(*msgPtr->info); i++) { if (msgPtr->info[i]) { Tcl_DecrRefCount(msgPtr->info[i]); } } ckfree(msgPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * CreateBodyInfo -- * * Create and somewhat initialize a BodyInfo structure. * * Results: * A pointer to a BodyInfo structure. * * Side effects: * None. * * *---------------------------------------------------------------------- */ BodyInfo* CreateBodyInfo(MessageInfo *msgPtr) { BodyInfo *bodyInfoPtr; int pad = sizeof(char*) - sizeof(BodyInfo)%sizeof(char*); if (sizeof(char*) == pad) { pad = 0; } bodyInfoPtr = (BodyInfo*)ckalloc(sizeof(BodyInfo)+pad+16); bodyInfoPtr->cmdName = (char*)bodyInfoPtr + pad + sizeof(BodyInfo); sprintf(bodyInfoPtr->cmdName, "RatBody%d", numBodies++); bodyInfoPtr->firstbornPtr = NULL; bodyInfoPtr->nextPtr = NULL; bodyInfoPtr->containedEntity = NULL; bodyInfoPtr->type = msgPtr->type; bodyInfoPtr->msgPtr = msgPtr; bodyInfoPtr->secPtr = NULL; bodyInfoPtr->altPtr = NULL; bodyInfoPtr->decodedTextPtr = NULL; bodyInfoPtr->encoded = 0; bodyInfoPtr->sigStatus = RAT_UNSIGNED; bodyInfoPtr->pgpOutput = NULL; return bodyInfoPtr; } /* *---------------------------------------------------------------------- * * RatBodyCmd -- * * Main bodypart entity procedure. This routine implements the * bodypart commands mentioned in ../INTERFACE. * * Results: * A standard tcl result. * * Side effects: * many. * * *---------------------------------------------------------------------- */ int RatBodyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { BodyInfo *bodyInfoPtr = (BodyInfo*) clientData; BODY *bodyPtr = bodyInfoPtr->bodyPtr; unsigned long length; Tcl_Obj *ov[2], *oPtr, *rPtr; if (objc < 2) { goto usage; } if (!strcmp(Tcl_GetString(objv[1]), "children")) { BodyInfo **partInfoPtrPtr, *partInfoPtr; if (TYPEMULTIPART != bodyPtr->type) { return TCL_OK; } if (!bodyInfoPtr->firstbornPtr) { (*messageProcInfo[bodyInfoPtr->type].makeChildrenProc) (interp, bodyInfoPtr); for (partInfoPtrPtr = &bodyInfoPtr->firstbornPtr; *partInfoPtrPtr; partInfoPtrPtr = &(*partInfoPtrPtr)->nextPtr) { RatPGPBodyCheck(interp, messageProcInfo, partInfoPtrPtr); Tcl_CreateObjCommand(interp, (*partInfoPtrPtr)->cmdName, RatBodyCmd, (ClientData)(*partInfoPtrPtr), NULL); } } rPtr = Tcl_NewObj(); for (partInfoPtr = bodyInfoPtr->firstbornPtr; partInfoPtr; partInfoPtr = partInfoPtr->nextPtr) { Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewStringObj(partInfoPtr->cmdName, -1)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "message")) { char *body; if (!bodyInfoPtr->containedEntity) { if (TYPEMESSAGE != bodyPtr->type && !strcasecmp(bodyPtr->subtype, "rfc822")) { Tcl_SetResult(interp, "Not an message/rfc822 bodypart", TCL_STATIC); return TCL_ERROR; } body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (body && *body) { bodyInfoPtr->containedEntity = RatFrMessageCreate(interp, body, length, NULL); Tcl_SetResult(interp, bodyInfoPtr->containedEntity,TCL_STATIC); } else { Tcl_SetResult(interp,"Failed to fetch mail body. " "The message is damaged", TCL_STATIC); return TCL_ERROR; } } else { Tcl_SetResult(interp, bodyInfoPtr->containedEntity, TCL_STATIC); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "type")) { Tcl_SetObjResult(interp, RatBodyType(bodyInfoPtr)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "params")) { PARAMETER *parameter; rPtr = Tcl_NewObj(); for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { ov[0] = Tcl_NewStringObj(parameter->attribute, -1); ov[1] = Tcl_NewStringObj(parameter->value, -1); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewListObj(2, ov)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "parameter")) { PARAMETER *parameter; if (objc != 3) goto usage; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if (0 == strcasecmp(Tcl_GetString(objv[2]),parameter->attribute)) { Tcl_SetResult(interp, parameter->value, TCL_VOLATILE); break; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "disp_type")) { Tcl_SetResult(interp, bodyPtr->disposition.type, TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "disp_parm")) { PARAMETER *parameter; rPtr = Tcl_NewObj(); for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { ov[0] = Tcl_NewStringObj(parameter->attribute, -1); ov[1] = Tcl_NewStringObj(parameter->value, -1); Tcl_ListObjAppendElement(interp, rPtr, Tcl_NewListObj(2, ov)); } Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "id")) { if (bodyPtr->id) { Tcl_SetResult(interp, bodyPtr->id, TCL_VOLATILE); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "description")) { if (bodyPtr->description) { char *desc = RatDecodeHeader(interp, bodyPtr->description, 0); Tcl_SetResult(interp, desc, TCL_VOLATILE); } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "size")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyPtr->size.bytes)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "lines")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyPtr->size.lines)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "encoding")) { char *enc; switch(bodyPtr->encoding) { case ENC7BIT: enc = "7bit"; break; case ENC8BIT: enc = "8bit"; break; case ENCBINARY: enc = "binary"; break; case ENCBASE64: enc = "base64"; break; case ENCQUOTEDPRINTABLE:enc = "quoted-printable"; break; default: enc = "unkown"; break; } Tcl_SetResult(interp, enc, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "isGoodCharset")) { PARAMETER *parameter; CONST84 char *charset = "us-ascii", *alias; int b; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { charset = parameter->value; break; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", charset, TCL_GLOBAL_ONLY))) { charset = alias; } b = RatGetEncoding(interp, charset) ? 1 : 0; Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "data")) { char *isCharset; int encoded; if (3 != objc && 4 != objc) goto usage; if (TCL_OK != Tcl_GetBooleanFromObj(interp, objv[2], &encoded)) { return TCL_ERROR; } if (objc == 4) { isCharset = Tcl_GetString(objv[3]); } else { isCharset = NULL; } Tcl_SetObjResult(interp, RatBodyData(interp, bodyInfoPtr, encoded, isCharset)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "saveData")) { int encoded, convertNL; Tcl_Channel channel; if (5 != objc) goto usage; if (NULL==(channel=Tcl_GetChannel(interp,Tcl_GetString(objv[2]),NULL)) || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[3], &encoded) || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[4], &convertNL)) { goto usage; } return RatBodySave(interp, channel, bodyInfoPtr, encoded, convertNL); } else if (!strcmp(Tcl_GetString(objv[1]), "dsn")) { char *body; int r; if (TYPEMESSAGE != bodyPtr->type && !strcasecmp(bodyPtr->subtype, "delivery-status")) { Tcl_SetResult(interp, "Not an message/delivery-status bodypart", TCL_STATIC); return TCL_ERROR; } body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (*body) { oPtr = Tcl_NewStringObj(body, length); r = RatDSNExtract(interp, oPtr); Tcl_DecrRefCount(oPtr); return r; } else { Tcl_SetResult(interp, "No body", TCL_STATIC); return TCL_ERROR; } } else if (!strcmp(Tcl_GetString(objv[1]), "getShowCharset")) { CONST84 char *c_charset = "us-ascii", *alias; PARAMETER *parmPtr; char *charset; if (TYPETEXT != bodyPtr->type) { Tcl_AppendElement(interp, "good"); Tcl_AppendElement(interp, "us-ascii"); return TCL_OK; } for (parmPtr = bodyPtr->parameter; parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "charset")) { c_charset = parmPtr->value; break; } } charset = cpystr(c_charset); lcase(charset); /* * - See if this charset is an alias and resolve that if so. * - Check if this is a charset we do have a font for * return good and the charset in that case. * - Check if this is a character set we know about and convert. * return lose if that is the case. * - return none. */ if (NULL != ( alias = Tcl_GetVar2(interp, "charsetAlias", charset, TCL_GLOBAL_ONLY))) { ckfree(charset); charset = cpystr(alias); } if (Tcl_GetVar2(interp, "fontEncoding", charset, TCL_GLOBAL_ONLY)) { ov[0] = Tcl_NewStringObj("good", 4); ov[1] = Tcl_NewStringObj("charset", -1); Tcl_SetObjResult(interp, Tcl_NewListObj(2, ov)); ckfree(charset); return TCL_OK; } /* * This converting part is not implemented yet */ ov[0] = Tcl_NewStringObj("none", 4); ov[1] = Tcl_NewStringObj("", 0); Tcl_SetObjResult(interp, Tcl_NewListObj(2, ov)); ckfree(charset); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "findShowCommand")) { return RatMcapFindCmd(interp, bodyInfoPtr); } else if (!strcmp(Tcl_GetString(objv[1]), "filename")) { PARAMETER *parmPtr; char *filename = NULL, *delim, *d; for (parmPtr = bodyPtr->disposition.parameter; parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "filename") || !strcasecmp(parmPtr->attribute, "name")) { filename = parmPtr->value; break; } } for (parmPtr = bodyPtr->parameter; !filename && parmPtr; parmPtr = parmPtr->next) { if (!strcasecmp(parmPtr->attribute, "filename") || !strcasecmp(parmPtr->attribute, "name")) { filename = parmPtr->value; break; } } if (!filename && bodyPtr->description && !strchr(bodyPtr->description, ' ')) { filename = bodyPtr->description; } if (filename) { d = RatDecodeHeader(interp, filename, 0); delim = strrchr(d, '/'); if (delim) { Tcl_SetResult(interp, delim+1, TCL_VOLATILE); } else { Tcl_SetResult(interp, d, TCL_VOLATILE); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "encoded")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(bodyInfoPtr->encoded)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "sigstatus")) { char *status = NULL; switch (bodyInfoPtr->sigStatus) { case RAT_UNSIGNED: status = "pgp_none"; break; case RAT_UNCHECKED: status = "pgp_unchecked"; break; case RAT_SIG_GOOD: status = "pgp_good"; break; case RAT_SIG_BAD: status = "pgp_bad"; break; } Tcl_SetResult(interp, status, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "checksig")) { RatPGPChecksig(interp, messageProcInfo, bodyInfoPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "getPGPOutput")) { if (bodyInfoPtr->pgpOutput) { Tcl_SetResult(interp, Tcl_DStringValue(bodyInfoPtr->pgpOutput), TCL_VOLATILE); } else { Tcl_ResetResult(interp); } return TCL_OK; } usage: Tcl_AppendResult(interp, "Illegal argument string", NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatBodyDelete -- * * Deletes the given body. * * Results: * None. * * Side effects: * The bodypart and all its siblings are deleted from the interpreter * and all the structures are freed. * * *---------------------------------------------------------------------- */ static void RatBodyDelete(Tcl_Interp *interp, BodyInfo *bodyInfoPtr) { BodyInfo *siblingInfoPtr, *nextSiblingInfoPtr; Tcl_DeleteCommand(interp, bodyInfoPtr->cmdName); siblingInfoPtr = bodyInfoPtr->firstbornPtr; (*messageProcInfo[bodyInfoPtr->type].bodyDeleteProc)(bodyInfoPtr); while (siblingInfoPtr) { nextSiblingInfoPtr = siblingInfoPtr->nextPtr; RatBodyDelete(interp, siblingInfoPtr); siblingInfoPtr = nextSiblingInfoPtr; } if (bodyInfoPtr->containedEntity) { RatMessageDelete(interp, bodyInfoPtr->containedEntity); } if (bodyInfoPtr->pgpOutput) { Tcl_DStringFree(bodyInfoPtr->pgpOutput); ckfree(bodyInfoPtr->pgpOutput); } ckfree(bodyInfoPtr); } /* *---------------------------------------------------------------------- * * RatMessageGet -- * * Retrieves a message in textual form. The text is placed in the * supplied Tcl_DString. * * Results: * No result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ void RatMessageGet(Tcl_Interp *interp, MessageInfo *msgPtr, Tcl_DString *ds, char *flags, size_t flaglen, char *date, size_t datelen) { char *data; int seen; Tcl_Obj *oPtr; data = (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr); Tcl_DStringAppend(ds, data, strlen(data)); Tcl_DStringAppend(ds, "\r\n", 2); if (msgPtr->folderInfoPtr) { seen = (*msgPtr->folderInfoPtr->getFlagProc)(msgPtr->folderInfoPtr, interp, msgPtr->msgNo, RAT_SEEN); } else { seen = 1; } data = (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr); Tcl_DStringAppend(ds, data, strlen(data)); if (!seen) { (*msgPtr->folderInfoPtr->setFlagProc)(msgPtr->folderInfoPtr, interp, msgPtr->msgNo, RAT_SEEN, 0); } if (flags) { oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_FLAGS, 0); strlcpy(flags, Tcl_GetString(oPtr), flaglen); oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_DATE_IMAP4, 0); strlcpy(date, Tcl_GetString(oPtr), datelen); } } /* *---------------------------------------------------------------------- * * RatInsertCmd -- * * Inserts the given message into the database * * Results: * A standard Tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatInsertCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_CmdInfo cmdInfo; MessageInfo *msgPtr; if (objc != 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " msgId keywords exDate exType\"", (char *) NULL); return TCL_ERROR; } if (0 == Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo)) { Tcl_AppendResult(interp, "No such message: ", Tcl_GetString(objv[1]), NULL); return TCL_ERROR; } msgPtr = (MessageInfo*)cmdInfo.objClientData; return RatInsertMsg(interp, msgPtr, Tcl_GetString(objv[2]), Tcl_GetString(objv[3]), Tcl_GetString(objv[4])); } /* *---------------------------------------------------------------------- * * RatInsertMsg -- * * Inserts the given message into the database * * Results: * A standard Tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatInsertMsg (Tcl_Interp *interp, MessageInfo *msgPtr, char *keywords, char *exDate, char *exType) { char *to, *from, *cc, *subject, *msgid, *ref, *flags, *key, *value; MESSAGECACHE elt; int listObjc, elemObjc; char *eFrom, *header, *body, *s, *e, *d; Tcl_DString dString; int result, i; struct tm tm; time_t date = 0, exTime; Tcl_Obj *oPtr, **listObjv, **elemObjv; to = from = cc = subject = msgid = ref = flags = NULL; if (TCL_OK != RatMessageGetHeader(interp, (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr))) { return TCL_ERROR; } oPtr = Tcl_GetObjResult(interp); Tcl_ListObjGetElements(interp, oPtr, &listObjc, &listObjv); for (i=0; i'))) { ref = (char*)ckalloc(e-s+1); strlcpy(ref, s, e-s+1); } else if (!strcasecmp(key, "in-reply-to") && (s = strchr(value, '<')) && (e = strchr(s, '>'))) { ckfree(ref); ref = (char*)ckalloc(e-s+1); strlcpy(ref, s, e-s+1); ref = cpystr(value); } else if (!strcasecmp(key, "status") || !strcasecmp(key, "x-status")) { if (flags) { flags = (char*)ckrealloc(flags, strlen(flags)+strlen(value)+1); strcpy(&flags[strlen(flags)], value); } else { flags = cpystr(value); } } else if (!strcasecmp(key, "date")) { if (T == mail_parse_date(&elt, value)) { tm.tm_sec = elt.seconds; tm.tm_min = elt.minutes; tm.tm_hour = elt.hours; tm.tm_mday = elt.day; tm.tm_mon = elt.month - 1; tm.tm_year = elt.year+70; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; date = (int)mktime(&tm); } else { date = 0; } } } if (flags) { for (s = d = flags; *s; s++) { if ('D' != *s && 'F' != *s) { *d++ = *s; } } *d = '\0'; } else { oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_UNIXFLAGS, 0); flags = cpystr(Tcl_GetString(oPtr)); } if (0 == date) { long myLong = 0; oPtr = (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, RAT_FOLDER_DATE_N, 0); Tcl_GetLongFromObj(interp, oPtr, &myLong); date = (time_t) myLong; } Tcl_DStringInit(&dString); eFrom = (*messageProcInfo[msgPtr->type].getEnvelopeProc)(interp, msgPtr); header = (*messageProcInfo[msgPtr->type].getHeadersProc)(interp, msgPtr); Tcl_DStringAppend(&dString, header, strlen(header)); Tcl_DStringAppend(&dString, "\r\n", 2); body = (*messageProcInfo[msgPtr->type].fetchTextProc)(interp, msgPtr); Tcl_DStringAppend(&dString, body, strlen(body)); Tcl_ResetResult(interp); exTime = atol(exDate); if (!strcmp("none", exType)) { exTime = 0; } result = RatDbInsert(interp, to, from, cc, msgid, ref, subject, date, flags, keywords, exTime, exType, eFrom, Tcl_DStringValue(&dString), Tcl_DStringLength(&dString)); Tcl_DStringFree(&dString); ckfree(to); ckfree(from); ckfree(cc); ckfree(msgid); ckfree(ref); ckfree(subject); ckfree(flags); return result; } /* *---------------------------------------------------------------------- * * RatParseList -- * * Parse a list expression (almost like a printf format string) * * Results: * A structure representing the parsed expression, or null if * there is a syntax error in the format string. * * Side effects: * None. * * *---------------------------------------------------------------------- */ ListExpression* RatParseList(const char *format) { ListExpression *expPtr; int i, w, expIndex, bufLen, num; char buf[1024]; for(i=num=0; '\0' != format[i]; i++) { if ('%' == format[i] && format[i+1] && '%' != format[i+1]) { while (format[++i] && ('-' == format[i] || isdigit((unsigned char)format[i]))); if (!strchr("snmrRbBdDSitM", format[i])) { return NULL; } num++; } } expPtr = (ListExpression*)ckalloc(sizeof(ListExpression)); expPtr->size = num; expPtr->preString = (char**)ckalloc(num*sizeof(char*)); expPtr->typeList = (RatFolderInfoType*)ckalloc(num*sizeof(RatFolderInfoType)); expPtr->fieldWidth = (int*)ckalloc(num*sizeof(int)); expPtr->leftJust = (int*)ckalloc(num*sizeof(int)); for (i = expIndex = bufLen = 0; format[i]; i++) { if ('%' == format[i]) { if ('%' == format[++i]) { buf[bufLen++] = format[i]; continue; } buf[bufLen] = '\0'; expPtr->preString[expIndex] = cpystr(buf); if ('-' == format[i]) { expPtr->leftJust[expIndex] = 1; i++; } else { expPtr->leftJust[expIndex] = 0; } w=0; while (isdigit((unsigned char)format[i])) { w = w*10+format[i++]-'0'; } expPtr->fieldWidth[expIndex] = w; switch(format[i]) { case 's': expPtr->typeList[expIndex++] = RAT_FOLDER_SUBJECT; break; case 'n': expPtr->typeList[expIndex++] = RAT_FOLDER_NAME; break; case 'm': expPtr->typeList[expIndex++] = RAT_FOLDER_MAIL; break; case 'r': expPtr->typeList[expIndex++] = RAT_FOLDER_NAME_RECIPIENT; break; case 'R': expPtr->typeList[expIndex++] = RAT_FOLDER_MAIL_RECIPIENT; break; case 'b': expPtr->typeList[expIndex++] = RAT_FOLDER_SIZE; break; case 'B': expPtr->typeList[expIndex++] = RAT_FOLDER_SIZE_F; break; case 'd': expPtr->typeList[expIndex++] = RAT_FOLDER_DATE_F; break; case 'D': expPtr->typeList[expIndex++] = RAT_FOLDER_DATE_N; break; case 'S': expPtr->typeList[expIndex++] = RAT_FOLDER_STATUS; break; case 'i': expPtr->typeList[expIndex++] = RAT_FOLDER_INDEX; break; case 't': expPtr->typeList[expIndex++] =RAT_FOLDER_THREADING;break; case 'M': expPtr->typeList[expIndex++] = RAT_FOLDER_MSGID; break; } bufLen = 0; } else { buf[bufLen++] = format[i]; } } if (bufLen) { buf[bufLen] = '\0'; expPtr->postString = cpystr(buf); } else { expPtr->postString = NULL; } return expPtr; } /* *---------------------------------------------------------------------- * * RatFreeListExpression -- * * Frees all memory associated with a list expression. * * Results: * None. * * Side effects: * Some memory is freed. * * *---------------------------------------------------------------------- */ void RatFreeListExpression(ListExpression *exPtr) { int i; for (i=0; isize; i++) { ckfree(exPtr->preString[i]); } ckfree(exPtr->preString); ckfree(exPtr->typeList); ckfree(exPtr->fieldWidth); ckfree(exPtr->leftJust); ckfree(exPtr->postString); ckfree(exPtr); } /* *---------------------------------------------------------------------- * * RatDoList -- * * Print the list information about a message. * * Results: * A tcl object * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatDoList(Tcl_Interp *interp, ListExpression *exprPtr, RatInfoProc *infoProc, ClientData clientData, int index) { Tcl_Obj *oPtr = Tcl_NewObj(), *iPtr; char *str; int i, j, slen, length; for (i=0; isize; i++) { if (exprPtr->preString[i]) { Tcl_AppendToObj(oPtr, exprPtr->preString[i], -1); } iPtr = (*infoProc)(interp, clientData, exprPtr->typeList[i], index); if (!iPtr) { for (j=0; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } continue; } if (exprPtr->fieldWidth[i]) { str = Tcl_GetStringFromObj(iPtr, &slen); length = Tcl_NumUtfChars(str, slen); if (length > exprPtr->fieldWidth[i]) { j = Tcl_UtfAtIndex(str, exprPtr->fieldWidth[i]) - str; Tcl_AppendToObj(oPtr, str, j); } else { if (exprPtr->leftJust[i]) { Tcl_AppendObjToObj(oPtr, iPtr); for (j=length; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } } else { for (j=length; jfieldWidth[i]; j++) { Tcl_AppendToObj(oPtr, " ", 1); } Tcl_AppendObjToObj(oPtr, iPtr); } } } else { Tcl_AppendObjToObj(oPtr, iPtr); } } if (exprPtr->postString) { Tcl_AppendToObj(oPtr, exprPtr->postString, -1); } return oPtr; } /* *---------------------------------------------------------------------- * * RatMsgInfo -- * * get information about a message * * Results: * A tcl object * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatMsgInfo(Tcl_Interp *interp, MessageInfo *msgPtr, RatFolderInfoType type) { return (*messageProcInfo[msgPtr->type].getInfoProc)(interp, (ClientData)msgPtr, type, 0); } /* *---------------------------------------------------------------------- * * RatBodySave -- * * Save a bodypart to an open channel. * * Results: * A standard tcl result. * * Side effects: * None. * * *---------------------------------------------------------------------- */ int RatBodySave(Tcl_Interp *interp,Tcl_Channel channel, BodyInfo *bodyInfoPtr, int encoded, int convertNL) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; char *body; int result = 0, i; unsigned long length; Tcl_DString *dsPtr = NULL; if (NULL == (body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length))) { Tcl_SetResult(interp, "[Body not available]\n", TCL_STATIC); return TCL_OK; } if (!encoded) { dsPtr = RatDecode(interp, bodyPtr->encoding, body, length, NULL); body =Tcl_DStringValue(dsPtr); length = Tcl_DStringLength(dsPtr); } if (convertNL) { /* * This isn't really elegant but since the channel is buffered * we shouldn't suffer too badly. */ for (i=0; inextPtr) { if (TYPETEXT == bodyInfoPtr->bodyPtr->type) { return bodyInfoPtr; } if (bodyInfoPtr->firstbornPtr && (NULL != (b2Ptr = RatFindFirstText(bodyInfoPtr->firstbornPtr)))) { return b2Ptr; } } return NULL; } /* *---------------------------------------------------------------------- * * RatGetCitation -- * * Get the citation to use * * Results: * A pointer to a citation string to use. This pointer will * remain valid until the next call to this function. * are found. * * Side effects: * May call userproc. * * *---------------------------------------------------------------------- */ static CONST84 char* RatGetCitation(Tcl_Interp *interp, MessageInfo *msgPtr) { Tcl_CmdInfo cmdInfo; static char citation[80]; if (0 != Tcl_GetCommandInfo(interp, "RatUP_Citation", &cmdInfo)) { if (TCL_OK != Tcl_VarEval(interp,"RatUP_Citation ",msgPtr->name,NULL)){ RatLog(interp, RAT_ERROR, Tcl_GetStringResult(interp), RATLOG_EXPLICIT); return ""; } if (79 < strlen(Tcl_GetStringResult(interp))) { RatLog(interp, RAT_ERROR, "Too long citation", RATLOG_EXPLICIT); return ""; } strlcpy(citation, Tcl_GetStringResult(interp), sizeof(citation)); return citation; } return Tcl_GetVar2(interp, "option", "reply_lead", TCL_GLOBAL_ONLY); } /* *---------------------------------------------------------------------- * * RatCiteMessage -- * * Copy a message and add citation. * * Results: * The cited text is appended to dstObjPtr * * Side effects: * Modifies *dstObjPtr * *---------------------------------------------------------------------- */ static void RatCiteMessage(Tcl_Interp *interp, Tcl_Obj *dstObjPtr, CONST84 char *src, CONST84 char *myCitation) { int i, skipSig, addCitBlank, myCitLength; Tcl_Obj *oPtr; CONST84 char *srcPtr; /* * Initialize and find out desired behaviour */ myCitLength = strlen(myCitation); if (' ' == myCitation[myCitLength-1]) { addCitBlank = 1; myCitLength--; } else { addCitBlank = 0; } oPtr = Tcl_GetVar2Ex(interp, "option", "skip_sig", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &skipSig); /* * Go over the text and add citation */ for (srcPtr = src; *srcPtr;) { /* * Stop when encoutering signature (if that option is true) */ if (skipSig && '-'== srcPtr[0] && '-' == srcPtr[1] && ' ' == srcPtr[2] && '\n' == srcPtr[3]) { break; } Tcl_AppendToObj(dstObjPtr, myCitation, myCitLength); if ('>' != *srcPtr && addCitBlank) { Tcl_AppendToObj(dstObjPtr, " ", 1); } for (i=0; '\n' != srcPtr[i] && srcPtr[i]; i++); Tcl_AppendToObj(dstObjPtr, srcPtr, i); srcPtr += i; if ('\n' == *srcPtr) { Tcl_AppendToObj(dstObjPtr, "\n", 1); srcPtr++; } } } /* *---------------------------------------------------------------------- * * RatWrapMessage -- * * Wraps the text of a message * * Results: * An opject containing the wrapped text is returned. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatWrapMessage(Tcl_Interp *interp, Tcl_Obj *textPtr) { int wrapLength, l, citLength, citLength2, overflow, i, add, mark, broken; CONST84 char *s, *e, *cPtr, *lineStartPtr, *startPtr, *citPtr = NULL; Tcl_RegExp citexp, bullexp; Tcl_Obj *nPtr = Tcl_NewObj(), *oPtr; oPtr = Tcl_GetVar2Ex(interp, "option", "wrap_length", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &wrapLength); s = Tcl_GetVar2(interp, "option", "citexp", TCL_GLOBAL_ONLY); citexp = Tcl_RegExpCompile(interp, s); if (NULL == citexp) { RatLogF(interp, RAT_ERROR, "illegal_regexp", RATLOG_EXPLICIT, Tcl_GetStringResult(interp)); } s = Tcl_GetVar2(interp, "option", "bullexp", TCL_GLOBAL_ONLY); bullexp = Tcl_RegExpCompile(interp, s); if (NULL == citexp) { RatLogF(interp, RAT_ERROR, "illegal_regexp", RATLOG_EXPLICIT, Tcl_GetStringResult(interp)); } for (cPtr = Tcl_GetString(textPtr); *cPtr;) { /* * Check if this line needs to be wrapped */ startPtr = cPtr; for (l=0; l < wrapLength && '\n' != *cPtr && *cPtr; l++, cPtr = Tcl_UtfNext(cPtr)); if (l < wrapLength) { Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); if ('\n' == *cPtr) { Tcl_AppendToObj(nPtr, "\n", 1); cPtr++; } continue; } /* * If it contains no letters after the wrap-point we keep it unwrapped */ for (s=cPtr; *s && '\n' != *s && !isalpha(*s); s = Tcl_UtfNext(s)); if (!*s || '\n' == *s) { Tcl_AppendToObj(nPtr, startPtr, s-startPtr); cPtr = s; if ('\n' == *cPtr) { Tcl_AppendToObj(nPtr, "\n", 1); cPtr++; } continue; } /* * It should be wrapped, find citation */ if (citexp && Tcl_RegExpExec(interp, citexp, startPtr, startPtr) && (Tcl_RegExpRange(citexp, 0, &s, &e), s == startPtr)) { citLength = e-s; citPtr = startPtr; } else { citLength = 0; } /* * Find point to break * First walk backwards until first LWSP. * Then check that we actually have some text left. * If not then do not bother wrapping this line */ for (; !isspace(*cPtr) && cPtr > startPtr+citLength; cPtr--); for (s = startPtr+citLength; s < cPtr && isspace(*s); s++); if (s == cPtr) { for (; !isspace(*cPtr) && *cPtr; cPtr++); Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); continue; } /* * Add first part of line and linebreak */ Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); Tcl_AppendToObj(nPtr, "\n", 1); mark = nPtr->length; Tcl_AppendToObj(nPtr, citPtr, citLength); /* * Continue adding the following lines. * Keep doing that until we find either: * An empty line (citation does not count) * A line whose citation differs in any non LWSP-character * A line whose indention is longer than the curent one, and * where the difference does not match a bullet expression */ lineStartPtr = startPtr = ++cPtr; l = citLength; broken = 1; while (*cPtr) { /* Found end of line? */ if ('\n' == *cPtr) { /* Skip trailing LWSP */ for (e = cPtr; isspace(*e) && e > startPtr; e--); if (e >= startPtr) e++; Tcl_AppendToObj(nPtr, startPtr, e-startPtr); cPtr++; /* Find length of citation */ if (citexp && Tcl_RegExpExec(interp, citexp, cPtr, cPtr) && (Tcl_RegExpRange(citexp, 0, &s, &e), s == cPtr)) { citLength2 = e-s; } else { citLength2 = 0; } add = 0; /* Check for empty line */ for (s=cPtr+citLength2; isspace(*s) && '\n' != *s && *s;s++); if (*s != '\n' && (isalnum(*s) || '\'' == *s || '"' == *s || '(' == *s)) { /* Is citation identical? */ if (citLength == citLength2 && !strncmp(cPtr, citPtr, citLength)) { add = 1; } if (citLength > citLength2) { /* Citation is short (by LWSP only) */ for (i=citLength2; i < citLength && isspace(citPtr[i]); i++); if (i == citLength) { /* * We have found a line with shorter citation. * Change data already inserted into dest as well * as remembered citation */ /* Data already there */ oPtr = Tcl_NewStringObj( nPtr->bytes+mark+citLength, nPtr->length-mark-citLength); Tcl_SetObjLength(nPtr, mark+citLength2); Tcl_AppendObjToObj(nPtr, oPtr); Tcl_DecrRefCount(oPtr); l -= citLength-citLength2; citLength = citLength2; add = 1; } } else if (citLength < citLength2 && Tcl_RegExpExec(interp, bullexp, citPtr+citLength, citPtr+citLength) && (Tcl_RegExpRange(bullexp, 0, &s, &e), 1) && citLength + e - s == citLength2) { /* Citation is longer and bullet exp matches */ /* Data already there */ oPtr = Tcl_NewStringObj( nPtr->bytes+mark+citLength, nPtr->length-mark-citLength); Tcl_SetObjLength(nPtr, mark); Tcl_AppendToObj(nPtr, cPtr, citLength2); Tcl_AppendObjToObj(nPtr, oPtr); Tcl_DecrRefCount(oPtr); l += citLength2-citLength; add = 1; citPtr = cPtr; citLength = citLength2; } } if (add && broken) { Tcl_AppendToObj(nPtr, " ", 1); l++; cPtr += citLength; startPtr = cPtr; broken = 0; continue; } else { Tcl_AppendToObj(nPtr, "\n", 1); l = 0; break; } } else if (l >= wrapLength) { broken = 1; for (; !isspace(*cPtr) && cPtr > startPtr; cPtr--); l = overflow = 0; if (cPtr == startPtr && startPtr == lineStartPtr) { while (!isspace(*cPtr)) cPtr++; overflow = 1; } Tcl_AppendToObj(nPtr, startPtr, cPtr-startPtr); Tcl_AppendToObj(nPtr, "\n", 1); if (startPtr != cPtr) { cPtr++; } lineStartPtr = startPtr = cPtr; if (overflow) break; mark = nPtr->length; Tcl_AppendToObj(nPtr, citPtr, citLength); l += citLength; } else { l++; cPtr = Tcl_UtfNext(cPtr); } } } return nPtr; } /* *---------------------------------------------------------------------- * * RatBodyType -- * * Gets the types of a bodypart * * Results: * A list object containing two strings, the first is the major * type and the second is the subtype. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatBodyType(BodyInfo *bodyInfoPtr) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; Tcl_Obj *oPtr[2]; oPtr[0] = Tcl_NewStringObj(body_types[bodyPtr->type], -1); if (bodyPtr->subtype) { oPtr[1] = Tcl_NewStringObj(bodyPtr->subtype, -1); } else { oPtr[1] = Tcl_NewStringObj("", 0); } return Tcl_NewListObj(2, oPtr); } /* *---------------------------------------------------------------------- * * RatBodyData -- * * Gets the content of a bodypart * * Results: * An object containing the data. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj* RatBodyData(Tcl_Interp *interp, BodyInfo *bodyInfoPtr, int encoded, char *charset) { BODY *bodyPtr = bodyInfoPtr->bodyPtr; Tcl_Obj *oPtr; char *body; CONST84 char *isCharset = NULL, *alias; PARAMETER *parameter; unsigned long length; if (charset) { isCharset = charset; } else if (TYPETEXT == bodyPtr->type){ isCharset = "us-ascii"; for (parameter = bodyPtr->parameter; parameter; parameter = parameter->next) { if ( 0 == strcasecmp("charset", parameter->attribute)) { isCharset = parameter->value; } } if ((alias = Tcl_GetVar2(interp, "charsetAlias", isCharset, TCL_GLOBAL_ONLY))) { isCharset = alias; } } body = (*messageProcInfo[bodyInfoPtr->type].fetchBodyProc) (bodyInfoPtr, &length); if (body) { if (encoded) { Tcl_Encoding enc; Tcl_DString ds; Tcl_DStringInit(&ds); if (ENC8BIT == bodyPtr->encoding) { enc = RatGetEncoding(interp, isCharset); Tcl_ExternalToUtfDString(enc, body, length, &ds); } else { Tcl_DStringAppend(&ds, body, length); } oPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); Tcl_DStringFree(&ds); } else { Tcl_DString *dsPtr = RatDecode(interp, bodyPtr->encoding, body, length, isCharset); oPtr = Tcl_NewStringObj(Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr)); Tcl_DStringFree(dsPtr); ckfree(dsPtr); } } else { oPtr = Tcl_NewStringObj("[Body not available]\n", -1); } return oPtr; } /* *---------------------------------------------------------------------- * * RatMessageInternalDate -- * * Gets the internal date of a message * * Results: * A pointer to a MESSAGECACHE entry where only the date-fields may * be used. May return NULL on errors. * * Side effects: * None. * *---------------------------------------------------------------------- */ MESSAGECACHE* RatMessageInternalDate(Tcl_Interp *interp, MessageInfo *msgPtr) { return (*messageProcInfo[msgPtr->type].getInternalDateProc)(interp,msgPtr); } /* *---------------------------------------------------------------------- * * RatPurgeFlags -- * * Purge Flagged, Deleted and Recent flags * * Results: * None. * * Side effects: * May modify the buffer passed as argument. However the result is * never bigger than the argument. * *---------------------------------------------------------------------- */ char* RatPurgeFlags(char *flags, int level) { char *cPtr, *toPurge[4]; int i, l; i = 0; if (1 == level) { toPurge[i++] = flag_name[RAT_FLAGGED].imap_name; toPurge[i++] = flag_name[RAT_DELETED].imap_name; toPurge[i++] = flag_name[RAT_RECENT].imap_name; } else { toPurge[i++] = flag_name[RAT_RECENT].imap_name; } toPurge[i] = NULL; for (i=0; '\0' != toPurge[i]; i++) { if (NULL != (cPtr = strstr(flags, toPurge[i]))) { l = strlen(toPurge[i]); if (flags == cPtr) { if (' ' == cPtr[l]) { l++; } } else { cPtr--; l++; } strcpy(cPtr, cPtr+l); } } return flags; } #ifdef MEM_DEBUG void ratMessageCleanup() { ckfree(messageProcInfo); } #endif /* MEM_DEBUG */