/* * ratFolder.c -- * * This file contains basic support code for the folder commands. Each * folder type is created using an unique command. This command returns * a folder handler, which when invoked calls the RatFolderCmd() * procedure with a pointer to a RatFolderInfo structure as clientData. * * TkRat software and its included text is Copyright 1996-2002 by * Martin Forssén * * The full text of the legal notices is contained in the file called * COPYRIGHT, included with this distribution. */ #include "ratFolder.h" /* * This structure is used to hold the data while sorting */ typedef struct SortData { char *msgid; char *ref; char *subject; char *sender; time_t date; long size; struct SortData *nextPtr; int prev, next, child, parent; Tcl_Obj *tPtr; } SortData; /* * Global list of folders */ RatFolderInfo *ratFolderList = NULL; /* * The number of folders opened. This is used when making * the folder entities. */ static int numFolders = 0; /* * Sort order names */ struct { SortOrder order; int reverse; char *name; } sortNames[] = { {SORT_THREADED, 0, "threaded"}, {SORT_SUBJDATE, 0, "subject"}, {SORT_SENDERDATE, 0, "sender"}, {SORT_SUBJECT, 0, "subjectonly"}, {SORT_SENDER, 0, "senderonly"}, {SORT_DATE, 0, "date"}, {SORT_NONE, 0, "folder"}, {SORT_NONE, 1, "reverseFolder"}, {SORT_DATE, 1, "reverseDate"}, {SORT_SIZE, 0, "size"}, {SORT_SIZE, 1, "reverseSize"}, {0, 0, NULL} }; /* * Flag names * The entries in this list must be synchronized with the enum type RatFlag * defined in RatFolder.h */ flag_name_t flag_name[] = { { "\\Seen", "seen", 'R' }, { "\\Deleted", "deleted", 'D' }, { "\\Flagged", "flagged", 'F' }, { "\\Answered", "answered", 'A' }, { "\\Draft", "draft", 'T' }, { "\\Recent", "recent", '\0' }, { NULL, NULL, '\0'} }; /* * Global variable used to controll the sorting functions */ static SortData *baseSortDataPtr; /* * Global id to handle folder list updates */ static int folderChangeId = 0; /* * Possible flag values for remote hosts */ #ifdef HAVE_OPENSSL static char *cClientFlags[] = { "/notls", "/ssl", "/novalidate-cert", "/secure", NULL }; #endif /* HAVE_OPENSSL */ static Tcl_ObjCmdProc RatOpenFolder; static Tcl_ObjCmdProc RatFolderCmd; static void RatFolderSort(Tcl_Interp *interp, RatFolderInfo *infoPtr); static int RatFolderSortCompareDate(const void *arg1, const void *arg2); static int RatFolderSortCompareSize(const void *arg1, const void *arg2); static int RatFolderSortCompareSubject(const void *arg1, const void *arg2); static int RatFolderSortCompareSender(const void *arg1, const void *arg2); static int IsChild(SortData *dataPtr, int child, int parent); static int RatFolderSortLinearize(int *p, int n, SortData *dataPtr, int first, int depth); static Tcl_TimerProc RatFolderUpdateTime; static Tcl_ObjCmdProc RatManageFolder; static RatFlag RatFlagNameToInt(const char *name); static char *RatGetIdentDef(Tcl_Interp *interp, Tcl_Obj *defPtr); /* *---------------------------------------------------------------------- * * RatFolderInit -- * * Initializes the folder commands. * * Results: * The return value is normally TCL_OK; if something goes wrong * TCL_ERROR is returned and an error message will be left in * the result area. * * Side effects: * The folder creation commands are created in interp. And the * Std folder is initialized. * * *---------------------------------------------------------------------- */ int RatFolderInit(Tcl_Interp *interp) { RatInitMessages(); if (TCL_OK != RatStdFolderInit(interp)) { return TCL_ERROR; } if (TCL_OK != RatDbFolderInit(interp)) { return TCL_ERROR; } if (TCL_OK != RatDisFolderInit(interp)) { return TCL_ERROR; } Tcl_CreateObjCommand(interp, "RatOpenFolder", RatOpenFolder, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "RatParseExp", RatParseExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatGetExp", RatGetExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatFreeExp", RatFreeExpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "RatCreateFolder", RatManageFolder, (void*)RAT_MGMT_CREATE, NULL); Tcl_CreateObjCommand(interp, "RatDeleteFolder", RatManageFolder, (void*)RAT_MGMT_DELETE, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * RatGetOpenFolder -- * * Search the list of open folders for a folder matching the given * definition. * * Results: * If a match is found then that folders reference count is incremented * and the infoPtr is returned. Otherwise NULL is returned. * * Side effects: * None. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatGetOpenFolder(Tcl_Interp *interp, Tcl_Obj *defPtr) { RatFolderInfo *infoPtr; char *def = RatGetIdentDef(interp, defPtr); for (infoPtr = ratFolderList; infoPtr && strcmp(infoPtr->definition, def); infoPtr = infoPtr->nextPtr); if (infoPtr) { infoPtr->refCount++; } return infoPtr; } /* *---------------------------------------------------------------------- * * RatOpenFolder -- * * See the INTERFACE specification * * Results: * The return value is normally TCL_OK and a foilder handle is left * in the result area; if something goes wrong TCL_ERROR is returned * and an error message will be left in the result area. * * Side effects: * The folder creation commands are created in interp. * * *---------------------------------------------------------------------- */ static int RatOpenFolder(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { RatFolderInfo *infoPtr; int i, fobjc, lobjc; CONST84 char *sortName = NULL; Tcl_Obj **fobjv, **lobjv, *role = NULL, *oPtr; if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " folderdef\"", (char *) NULL); return TCL_ERROR; } /* * Check open folders */ if ((infoPtr = RatGetOpenFolder(interp, objv[1]))) { Tcl_SetResult(interp, infoPtr->cmdName, TCL_VOLATILE); return TCL_OK; } Tcl_ListObjGetElements(interp, objv[1], &fobjc, &fobjv); if (!strcmp(Tcl_GetString(fobjv[1]), "dbase")) { infoPtr = RatDbFolderCreate(interp, objv[1]); } else if (!strcmp(Tcl_GetString(fobjv[1]), "dis")) { infoPtr = RatDisFolderCreate(interp, objv[1]); } else { infoPtr = RatStdFolderCreate(interp, objv[1]); } if (NULL == infoPtr) { Tcl_AppendResult(interp, "Failed to create folder", NULL); return TCL_ERROR; } Tcl_ListObjGetElements(interp, fobjv[2], &lobjc, &lobjv); for (i=0; i < lobjc; i+=2) { if (!strcmp("sort", Tcl_GetString(lobjv[i]))) { sortName = Tcl_GetString(lobjv[i+1]); } if (!strcmp("role", Tcl_GetString(lobjv[i]))) { role = lobjv[i+1]; } } infoPtr->definition = cpystr(RatGetIdentDef(interp, objv[1])); ckfree(infoPtr->name); infoPtr->name = cpystr(Tcl_GetString(fobjv[0])); infoPtr->refCount = 1; oPtr = Tcl_GetVar2Ex(interp, "option", "watcher_time", TCL_GLOBAL_ONLY); if (TCL_OK != Tcl_GetIntFromObj(interp, oPtr, &infoPtr->watcherInterval)) { infoPtr->watcherInterval = 30; } if (infoPtr->watcherInterval > 1000000) { infoPtr->watcherInterval = 1000000; } if (!sortName || !strcmp("default", sortName)) { sortName = Tcl_GetVar2(interp, "option","folder_sort",TCL_GLOBAL_ONLY); } for (i=0; sortNames[i].name && strcmp(sortNames[i].name, sortName); i++); if (sortNames[i].name) { infoPtr->sortOrder = sortNames[i].order; infoPtr->reverse = sortNames[i].reverse; } else { infoPtr->sortOrder = SORT_NONE; infoPtr->reverse = 0; } if (!role || !strcmp("default", Tcl_GetString(role))) { role = Tcl_GetVar2Ex(interp, "option", "default_role",TCL_GLOBAL_ONLY); } infoPtr->role = role; Tcl_IncrRefCount(infoPtr->role); infoPtr->sortOrderChanged = 0; infoPtr->cmdName = ckalloc(16); infoPtr->allocated = infoPtr->number; infoPtr->msgCmdPtr = (char **) ckalloc(infoPtr->allocated*sizeof(char*)); infoPtr->privatePtr = (ClientData**)ckalloc( infoPtr->allocated*sizeof(ClientData)); for (i=0; iallocated; i++) { infoPtr->msgCmdPtr[i] = (char *) NULL; infoPtr->privatePtr[i] = (ClientData*) NULL; } (*infoPtr->initProc)(infoPtr, interp, -1); infoPtr->presentationOrder = (int*)ckalloc(infoPtr->allocated*sizeof(int)); infoPtr->hidden = (int*) ckalloc(infoPtr->allocated*sizeof(int)); infoPtr->flagsChanged = 0; infoPtr->nextPtr = ratFolderList; if (infoPtr->finalProc) { (*infoPtr->finalProc)(infoPtr, interp); } ratFolderList = infoPtr; RatFolderSort(interp, infoPtr); sprintf(infoPtr->cmdName, "RatFolder%d", numFolders++); Tcl_CreateObjCommand(interp, infoPtr->cmdName, RatFolderCmd, (ClientData) infoPtr, (Tcl_CmdDeleteProc *) NULL); Tcl_SetVar2Ex(interp, "folderExists", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->visible), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderChanged", infoPtr->cmdName, Tcl_NewIntObj(++folderChangeId), TCL_GLOBAL_ONLY); Tcl_SetResult(interp, infoPtr->cmdName, TCL_VOLATILE); if (infoPtr->watcherInterval) { infoPtr->timerToken = Tcl_CreateTimerHandler( infoPtr->watcherInterval*1000, RatFolderUpdateTime, (ClientData)infoPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatFolderCmd -- * * Main folder entity procedure. This procedure implements the * folder commands mentioned in ../doc/interface. In order to make * this a tad easier it uses the procedures defined in the * RatFolderInfo structure :-) * * Results: * Depends on the input :-) * * Side effects: * The specified folder may be modified. * * *---------------------------------------------------------------------- */ int RatFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { RatFolderInfo *infoPtr = (RatFolderInfo*) clientData; Tcl_Obj *oPtr; if (objc < 2) goto usage; if (!strcmp(Tcl_GetString(objv[1]), "update")) { RatUpdateType mode; if (objc != 3) goto usage; if (!strcmp(Tcl_GetString(objv[2]), "update")) { mode = RAT_UPDATE; } else if (!strcmp("checkpoint", Tcl_GetString(objv[2]))) { if (!infoPtr->flagsChanged) { Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); return TCL_OK; } mode = RAT_CHECKPOINT; infoPtr->flagsChanged = 0; } else if (!strcmp(Tcl_GetString(objv[2]), "sync")) { mode = RAT_SYNC; } else { goto usage; } return RatUpdateFolder(interp, infoPtr, mode); } else if (!strcmp(Tcl_GetString(objv[1]), "close")) { int force = 0; if (objc != 2 && (objc != 3 || TCL_OK != Tcl_GetBooleanFromObj(interp,objv[2],&force))) { goto usage; } return RatFolderClose(interp, infoPtr, force); } else if (!strcmp(Tcl_GetString(objv[1]), "setName")) { if (objc != 3) goto usage; ckfree(infoPtr->name); infoPtr->name = (char *) ckalloc(strlen(Tcl_GetString(objv[2]))+1); strcpy(infoPtr->name, Tcl_GetString(objv[2])); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "info")) { int infoArgc; CONST84 char *infoArgv[3]; char numberBuf[16], sizeBuf[16]; char *list; if (objc != 2) goto usage; sprintf(numberBuf, "%d", infoPtr->visible); sprintf(sizeBuf, "%d", infoPtr->size); infoArgv[0] = infoPtr->name; infoArgv[1] = numberBuf; infoArgv[2] = sizeBuf; infoArgc = 3; list = Tcl_Merge(infoArgc, infoArgv); Tcl_SetResult(interp, list, TCL_DYNAMIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "list")) { ListExpression *exprPtr; Tcl_Obj *oPtr, *rPtr; int i; if (objc != 3) goto usage; if (NULL == (exprPtr = RatParseList(Tcl_GetString(objv[2])))) { Tcl_SetResult(interp, "Illegal list format", TCL_STATIC); return TCL_ERROR; } rPtr = Tcl_NewObj(); for (i=0; i < infoPtr->visible; i++) { oPtr = RatDoList(interp, exprPtr, infoPtr->infoProc, (ClientData)infoPtr, infoPtr->presentationOrder[i]); Tcl_ListObjAppendElement(interp, rPtr, oPtr); } RatFreeListExpression(exprPtr); Tcl_SetObjResult(interp, rPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "get")) { int index; if (objc != 3 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&index)) goto usage; if (index < 0 || index >= infoPtr->visible) { Tcl_SetResult(interp, "Index is out of bounds", TCL_STATIC); return TCL_ERROR; } if (NULL == infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]]) { infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]] = (*infoPtr->createProc)(infoPtr, interp, infoPtr->presentationOrder[index]); } Tcl_SetResult(interp, infoPtr->msgCmdPtr[infoPtr->presentationOrder[index]], TCL_VOLATILE); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "setFlag")) { int index, value, unseen, recent; RatFlag flag; if (objc != 5 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&index) || TCL_OK != Tcl_GetBooleanFromObj(interp, objv[4], &value)) { goto usage; } if (index < 0 || index >= infoPtr->visible) { Tcl_SetResult(interp, "Index is out of bounds", TCL_STATIC); return TCL_ERROR; } flag = RatFlagNameToInt(Tcl_GetString(objv[3])); if (value != (*infoPtr->getFlagProc)(infoPtr, interp, infoPtr->presentationOrder[index], flag)) { recent = infoPtr->recent; unseen = infoPtr->unseen; (*infoPtr->setFlagProc)(infoPtr, interp, infoPtr->presentationOrder[index], flag, value); infoPtr->flagsChanged = 1; if (infoPtr->recent != recent) { Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); } if (infoPtr->unseen != unseen) { oPtr = Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "getFlag")) { int index; RatFlag flag; if (objc != 4 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&index)) goto usage; if (index < 0 || index >= infoPtr->visible) { Tcl_SetResult(interp, "Index is out of bounds", TCL_STATIC); return TCL_ERROR; } flag = RatFlagNameToInt(Tcl_GetString(objv[3])); Tcl_SetObjResult(interp, Tcl_NewIntObj( (*infoPtr->getFlagProc)(infoPtr, interp, infoPtr->presentationOrder[index],flag))); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "flagged")) { RatFlag flag; char buf[64]; int i; if (objc != 3) goto usage; flag = RatFlagNameToInt(Tcl_GetString(objv[2])); Tcl_ResetResult(interp); for (i=0; ivisible; i++) { if ((*infoPtr->getFlagProc)(infoPtr, interp, infoPtr->presentationOrder[i], flag)) { sprintf(buf, "%d", i); Tcl_AppendElement(interp, buf); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "insert")) { Tcl_CmdInfo cmdInfo; char **msgv; int i, ret; if (objc < 3) goto usage; for(i=2; itype, TCL_STATIC); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "find")) { int msgNo, i; if (objc != 3) goto usage; Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); for (msgNo=0; msgNo < infoPtr->number; msgNo++) { if (infoPtr->msgCmdPtr[msgNo] && !strcmp(infoPtr->msgCmdPtr[msgNo], Tcl_GetString(objv[2]))) { for (i=0; msgNo != infoPtr->presentationOrder[i]; i++); Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); break; } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "match")) { int i, expId; if (objc != 3 || TCL_OK != Tcl_GetIntFromObj(interp, objv[2],&expId)) goto usage; oPtr = Tcl_NewObj(); for (i=0; inumber; i++) { if (RatExpMatch(interp, expId, infoPtr->infoProc, (ClientData)infoPtr, infoPtr->presentationOrder[i])) { Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewIntObj(i)); } } Tcl_SetObjResult(interp, oPtr); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "setSortOrder")) { CONST84 char *name = Tcl_GetString(objv[2]); int i, j; if (objc != 3) goto usage; if (!strcmp("default", name)) { name = Tcl_GetVar2(interp, "option","folder_sort",TCL_GLOBAL_ONLY); } for (i=0; sortNames[i].name && strcmp(sortNames[i].name, name); i++); if (sortNames[i].name) { /* * If the old sort order was threaded and the new one is not then * we should clear the threading info */ if (infoPtr->sortOrder == SORT_THREADED && sortNames[i].order != SORT_THREADED) { for (j=0; jnumber; j++) { (*infoPtr->setInfoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_THREADING, j, NULL); } } infoPtr->sortOrder = sortNames[i].order; infoPtr->reverse = sortNames[i].reverse; infoPtr->sortOrderChanged = 1; return TCL_OK; } else { Tcl_SetResult(interp, "No such sort order", TCL_STATIC); return TCL_ERROR; } } else if (!strcmp(Tcl_GetString(objv[1]), "getSortOrder")) { int i; for (i=0; sortNames[i].name; i++) { if (infoPtr->sortOrder == sortNames[i].order && infoPtr->reverse == sortNames[i].reverse) { Tcl_SetResult(interp, sortNames[i].name, TCL_STATIC); } } return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "netsync")) { if (!infoPtr->syncProc) { Tcl_AppendResult(interp, "Operation unsupported on this folder", (char *) NULL); return TCL_ERROR; } return (*infoPtr->syncProc)(infoPtr, interp); } else if (!strcmp(Tcl_GetString(objv[1]), "refcount")) { Tcl_SetObjResult(interp, Tcl_NewIntObj(infoPtr->refCount)); return TCL_OK; } else if (!strcmp(Tcl_GetString(objv[1]), "role")) { Tcl_SetObjResult(interp, infoPtr->role); return TCL_OK; } usage: Tcl_AppendResult(interp, "Illegal usage of \"", Tcl_GetString(objv[0]), "\"", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * RatFolderSort -- * * Sorts the folder according to the users wishes. The user may * communicates their will via the folder_sort variable. Currently * The following methods are implemented: * subjectonly - Alphabetically on subject * sender - Alphabetically on sender name * folder - Sorts in native folder order * reverseFolder - The reverse of the above * date - By date sent * reverseDate - By reverse date sent * subject - Group messages with the same subject * and sort the groups by the earliest date * in each group. * * Results: * None. * * Side effects: * The presentation order member of the RatFolderInfo structure * is initialized. The size of the folder is updated. * * TODO, convert to Tcl_Obj * *---------------------------------------------------------------------- */ static void RatFolderSort(Tcl_Interp *interp, RatFolderInfo *infoPtr) { int i, j, k, pi, pj, snarf, numParm, seen, *tmpPtr, first, last, *p=infoPtr->presentationOrder, needDate=0, needSubject=0, needSender=0, needIds = 0, needSize = 0, *uniqList, uniqListUsed, *subList, *lengthList; SortData *dataPtr, *dPtr; Tcl_Obj *oPtr, *o2Ptr; if (0 == infoPtr->number) { infoPtr->visible = 0; return; } oPtr = Tcl_GetVar2Ex(interp, "option","dsn_snarf_reports",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &snarf); switch(infoPtr->sortOrder) { case SORT_NONE: break; case SORT_SUBJECT: needSubject = 1; break; case SORT_THREADED: needIds = 1; needDate = 1; needSubject = 1; break; case SORT_SUBJDATE: needDate = 1; needSubject = 1; break; case SORT_SENDER: needSender = 1; break; case SORT_SENDERDATE: needDate = 1; needSender = 1; break; case SORT_DATE: needDate = 1; break; case SORT_SIZE: needSize = 1; break; } dataPtr = (SortData*)ckalloc(infoPtr->number*sizeof(*dataPtr)); infoPtr->size = 0; for (i=0; inumber; i++) { infoPtr->hidden[i] = 0; infoPtr->presentationOrder[i] = i; oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_TYPE, i); if (oPtr && !strcasecmp(Tcl_GetString(oPtr), "multipart/report")) { oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_PARAMETERS, i); if (!oPtr) { continue; } Tcl_ListObjLength(interp, oPtr, &numParm); for (j=0; jgetFlagProc)(infoPtr, interp, i, RAT_SEEN); if (0 == seen) { (*infoPtr->setFlagProc)(infoPtr, interp, i, RAT_SEEN, 1); } if (!infoPtr->msgCmdPtr[i]) { infoPtr->msgCmdPtr[i] = (*infoPtr->createProc)(infoPtr, interp, i); } if (RatDSNHandle(interp, infoPtr->msgCmdPtr[i]) && snarf) { if ((*infoPtr->getFlagProc)( infoPtr, interp, i, RAT_RECENT)){ infoPtr->recent--; } infoPtr->hidden[i] = 1; (*infoPtr->setFlagProc)(infoPtr, interp, i, RAT_DELETED, 1); } else { (*infoPtr->setFlagProc)(infoPtr, interp, i, RAT_SEEN, seen); } break; } } } if (!infoPtr->hidden[i]) { oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_SIZE,i) ; if (oPtr) { Tcl_GetIntFromObj(interp, oPtr, &j); infoPtr->size += j; } } if (needSubject) { oPtr = (*infoPtr->infoProc)(interp, (ClientData)infoPtr, RAT_FOLDER_CANONSUBJECT, i); dataPtr[i].subject = Tcl_GetString(oPtr); } if (needSender) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_NAME, i); dataPtr[i].sender = Tcl_GetString(oPtr); dataPtr[i].sender = cpystr(dataPtr[i].sender); lcase(dataPtr[i].sender); } if (needDate) { long myLong; oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_DATE_N, i); Tcl_GetLongFromObj(interp, oPtr, &myLong); dataPtr[i].date = (time_t) myLong; } if (needSize) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_SIZE, i); Tcl_GetLongFromObj(interp, oPtr, &dataPtr[i].size); } if (needIds) { oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_MSGID, i); if (oPtr) { dataPtr[i].msgid = Tcl_GetString(oPtr); } else { dataPtr[i].msgid = ""; } oPtr = (*infoPtr->infoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_REF, i); if (oPtr) { dataPtr[i].ref = Tcl_GetString(oPtr); } else { dataPtr[i].ref = ""; } } } baseSortDataPtr = dataPtr; switch (infoPtr->sortOrder) { case SORT_NONE: for (i=0; inumber; i++) { p[i] = i; } break; case SORT_THREADED: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareDate); /*for (i=0; inumber; i++) { printf("Msg: %d (really %d)\n", i, p[i]); printf(" Subj: %s\n", dataPtr[p[i]].subject); printf("MsgId: <%s>\n", dataPtr[p[i]].msgid); printf(" Ref: <%s>\n", (dataPtr[p[i]].ref ? dataPtr[p[i]].ref : "(NULL)")); }*/ /* * Start by sorting on hard references */ dataPtr[p[0]].prev = -1; dataPtr[p[0]].next = -1; dataPtr[p[0]].child = -1; dataPtr[p[0]].parent = -1; dataPtr[p[0]].tPtr = NULL; first = last = p[0]; for (i=1; inumber; i++) { pi = p[i]; dataPtr[pi].tPtr = NULL; dataPtr[pi].child = -1; dataPtr[pi].prev = last; dataPtr[last].next = pi; dataPtr[pi].next = -1; dataPtr[pi].child = -1; dataPtr[pi].parent = -1; last = pi; /* Find any replies to the current message */ for (j=i-1; j >= 0 && *dataPtr[pi].msgid; j--) { pj = p[j]; if (!strcmp(dataPtr[pj].ref, dataPtr[pi].msgid) && -1 == dataPtr[pj].parent) { /* Here 'pj' is considered a reply to 'pi' */ if (dataPtr[pj].prev != -1) { dataPtr[dataPtr[pj].prev].next = dataPtr[pj].next; } if (dataPtr[pj].next != -1) { dataPtr[dataPtr[pj].next].prev = dataPtr[pj].prev; } if (first == pj) first = dataPtr[pj].next; if (dataPtr[pi].child != -1) { dataPtr[dataPtr[pi].child].prev = pj; dataPtr[pj].next = dataPtr[pi].child; } else { dataPtr[pj].next = -1; } dataPtr[pi].child = pj; dataPtr[pj].prev = -1; dataPtr[pj].parent = pi; } } /* Find message which the current message is a reply to */ for (j=0; jnumber; i++) { pi = p[i]; if (-1 != dataPtr[pi].parent) continue; for (j=i-1; j>=0; j--) { pj = p[j]; if (!strcmp(dataPtr[pi].subject, dataPtr[pj].subject) && !IsChild(dataPtr, pj, pi)) { /* * 'pi' is later in the same thread as 'pj' * First we remove it from the list. */ dataPtr[dataPtr[pi].prev].next = dataPtr[pi].next; if (dataPtr[pi].next != -1) { dataPtr[dataPtr[pi].next].prev = dataPtr[pi].prev; } /* * If the parent to 'pj' also has the same subject * then we add this message after 'pj', otherwise * we add it under 'pj' */ for (k=pj; -1 != dataPtr[k].prev; k = dataPtr[k].prev); if (-1 != dataPtr[k].parent && !strcmp(dataPtr[pi].subject, dataPtr[dataPtr[k].parent].subject)) { dataPtr[pi].prev = pj; dataPtr[pi].next = dataPtr[pj].next; if (dataPtr[pj].next != -1) { dataPtr[dataPtr[pj].next].prev = pi; } dataPtr[pj].next = pi; } else { dataPtr[pi].parent = pj; dataPtr[pi].prev = -1; dataPtr[pi].next = dataPtr[pj].child; if (-1 != dataPtr[pj].child) { dataPtr[dataPtr[pj].child].prev = pi; dataPtr[dataPtr[pj].child].parent = -1; } dataPtr[pj].child = pi; } break; } } } /*printf("First: %d\n", first); for (i=0; inumber; i++) { printf("%d:\tprev: %2d next: %2d parent: %2d child: %2d\n", i, dataPtr[i].prev, dataPtr[i].next, dataPtr[i].parent, dataPtr[i].child); }*/ RatFolderSortLinearize(p, infoPtr->number, dataPtr, first, 0); for (i=0; inumber; i++) { (*infoPtr->setInfoProc)(interp,(ClientData)infoPtr, RAT_FOLDER_THREADING, i, dataPtr[i].tPtr); } break; case SORT_SUBJDATE: case SORT_SENDERDATE: /* * This algorithm is complicated: * - First we build a list of unique subjects in uniqList. Each entry * in this list contains the index of the first message with this * subject. The messages are linked with the nextPtr field in * the SortData structs. * - Then we sort each found subject. This is done by placing the * indexes of the messages in subList. And sort that. When it * is sorted we rebuild the subject chains via the nextPtr; * - After that we sort the first message in each subject. This is done * by reusing the uniqList. We replace each entry in it with a * pointer to the first entry in the set. Actually we do this in * the preceding step. Then we sort this list. * - Finally we build to result array. */ uniqList = (int*)ckalloc(2*infoPtr->number*sizeof(*uniqList)); subList = &uniqList[infoPtr->number]; lengthList = &uniqList[2*infoPtr->number]; for (i=uniqListUsed=0; inumber; i++) { for (j=0; jsortOrder == SORT_SUBJDATE ? !strcmp(dataPtr[i].subject, dataPtr[uniqList[j]].subject) : !strcmp( dataPtr[i].sender, dataPtr[uniqList[j]].sender)) { dataPtr[i].nextPtr = dataPtr[uniqList[j]].nextPtr; dataPtr[uniqList[j]].nextPtr = &dataPtr[i]; break; } } if (j == uniqListUsed) { dataPtr[i].nextPtr = NULL; uniqList[uniqListUsed++] = i; } } for (i=0; inextPtr) { subList[j++] = dPtr-dataPtr; } qsort((void*)subList, j, sizeof(int),RatFolderSortCompareDate); for (k=0; knextPtr) { p[k++] = dPtr-baseSortDataPtr; } } ckfree(uniqList); break; case SORT_SENDER: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p,infoPtr->number,sizeof(int),RatFolderSortCompareSender); break; case SORT_SUBJECT: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int), RatFolderSortCompareSubject); break; case SORT_DATE: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareDate); break; case SORT_SIZE: for (i=0; inumber; i++) { p[i] = i; } qsort((void*)p, infoPtr->number, sizeof(int),RatFolderSortCompareSize); } if (infoPtr->reverse) { tmpPtr = (int*)ckalloc(infoPtr->number*sizeof(int)); for (i=infoPtr->number-1, j=0; i >= 0; i--) { if (!infoPtr->hidden[p[i]]) { tmpPtr[j++] = p[i]; } } memcpy(p, tmpPtr, j*sizeof(int)); ckfree(tmpPtr); } else { for (i=j=0; i < infoPtr->number; i++) { if (!infoPtr->hidden[p[i]]) { p[j++] = p[i]; } } } infoPtr->visible = j; /* * Cleanup dataPtr */ for (i=0; inumber; i++) { if (needSender) { ckfree(dataPtr[i].sender); } } ckfree(dataPtr); } /* *---------------------------------------------------------------------- * * RatFolderSortCompare* -- * * This is the comparison functions used by RatFolderSort. They * expect to get pointers to integers as argumens. The integers * pointed at are indexes into a list of SortData structs which can be * found at the address in baseSortDataPtr. * * Results: * An integers describing the order of the compared objects. * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int RatFolderSortCompareDate(const void *arg1, const void *arg2) { return baseSortDataPtr[*((int*)arg1)].date - baseSortDataPtr[*((int*)arg2)].date; } static int RatFolderSortCompareSubject(const void *arg1, const void *arg2) { return strcmp(baseSortDataPtr[*((int*)arg1)].subject, baseSortDataPtr[*((int*)arg2)].subject); } static int RatFolderSortCompareSender(const void *arg1, const void *arg2) { return strcmp(baseSortDataPtr[*((int*)arg1)].sender, baseSortDataPtr[*((int*)arg2)].sender); } static int RatFolderSortCompareSize(const void *arg1, const void *arg2) { return baseSortDataPtr[*((int*)arg1)].size - baseSortDataPtr[*((int*)arg2)].size; } /* *---------------------------------------------------------------------- * * RatFolderCanonalizeSubject -- * * Copy a subject line and remove certain constructs (the re:). * * Results: * A new object reference * * Side effects: * None. * * *---------------------------------------------------------------------- */ Tcl_Obj* RatFolderCanonalizeSubject (const char *s) { const char *e; Tcl_Obj *nPtr; int len; if (s) { /* * We first try to find the start of the actual text (i.e. without any * leading Re:'s and whitespaces. Then we find how long the text is * (ignore trailing whitespaces). */ len = strlen(s); e = s+len-1; while (*s) { while (*s && s < e && isspace((unsigned char)*s)) s++, len--; if (!strncasecmp(s, "re", 2) && (':' == s[2] || isspace((unsigned char)s[2]))) { s += 2; len -= 2; if (*s == ':') { s++; len--; } } else { break; } } while (isspace((unsigned char)*e) && e > s) { e--; len--; } nPtr = Tcl_NewStringObj(s, len); len = Tcl_UtfToLower(Tcl_GetString(nPtr)); Tcl_SetObjLength(nPtr, len); return nPtr; } else { return Tcl_NewStringObj("", 0); } } /* *---------------------------------------------------------------------- * * IsChild -- * * See if one message has the other as one of its ancestors. * * Results: * A non-zero value if the child is related to the parent * * Side effects: * None. * * *---------------------------------------------------------------------- */ static int IsChild(SortData *dataPtr, int child, int parent) { int i; for (i=child; i > -1 && i != parent;) { if (-1 != dataPtr[i].parent) { i = dataPtr[i].parent; } else { i = dataPtr[i].prev; } } return (i == parent); } /* *---------------------------------------------------------------------- * * RatFolderSortLinearize -- * * Linearizes the linked list of messages. The list is also sorted. * * Results: * Number of elements added to p * * Side effects: * Modifies the p array. * * *---------------------------------------------------------------------- */ static int RatFolderSortLinearize(int *p, int n, SortData *dataPtr, int first, int depth) { int *s = (int*)ckalloc(sizeof(int)*n); int i, j, k, o, ns; char *c; for (i=first, ns=0; -1 != i; i = dataPtr[i].next) { s[ns++] = i; } qsort((void*)s, ns, sizeof(int), RatFolderSortCompareDate); for (i=j=0; isubject,0), -1); break; case RAT_FOLDER_CANONSUBJECT: oPtr = RatFolderCanonalizeSubject( RatDecodeHeader(interp,envPtr->subject,0)); break; case RAT_FOLDER_MAIL_REAL: for (adrPtr = envPtr->from; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { oPtr = Tcl_NewStringObj(NULL, 0); } else { oPtr = Tcl_NewStringObj(RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_NAME: if (!envPtr->from) { oPtr = Tcl_NewObj(); break; } if (RAT_ISME_YES == msgPtr->fromMe || (RAT_ISME_UNKOWN == msgPtr->fromMe && RatAddressIsMe(interp, envPtr->from, 1))) { msgPtr->fromMe = RAT_ISME_YES; if (envPtr->to && envPtr->to->personal) { oPtr = Tcl_GetVar2Ex(interp, "t", "to", TCL_GLOBAL_ONLY); if (Tcl_IsShared(oPtr)) { oPtr = Tcl_DuplicateObj(oPtr); } Tcl_AppendToObj(oPtr, ": ", 2); Tcl_AppendToObj(oPtr, RatDecodeHeader(interp, envPtr->to->personal, 0), -1); break; } } else { msgPtr->fromMe = RAT_ISME_NO; if (envPtr->from->personal) { oPtr = Tcl_NewStringObj(RatDecodeHeader(interp, envPtr->from->personal, 0), -1); break; } } /* fallthrough */ case RAT_FOLDER_MAIL: oPtr = Tcl_NewObj(); if (RAT_ISME_YES == msgPtr->fromMe || (RAT_ISME_UNKOWN == msgPtr->fromMe && RatAddressIsMe(interp, envPtr->from, 1))) { msgPtr->fromMe = RAT_ISME_YES; adrPtr = envPtr->to; Tcl_AppendObjToObj(oPtr, Tcl_GetVar2Ex(interp, "t", "to", TCL_GLOBAL_ONLY)); Tcl_AppendToObj(oPtr, ": ", 2); } else { msgPtr->fromMe = RAT_ISME_NO; adrPtr = envPtr->from; } for (; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { Tcl_DecrRefCount(oPtr); oPtr = Tcl_NewObj(); } else { Tcl_AppendToObj(oPtr, RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_NAME_RECIPIENT: if (!envPtr->to) { oPtr = Tcl_NewObj(); break; } msgPtr->toMe = RAT_ISME_NO; if (envPtr->to->personal) { oPtr = Tcl_NewStringObj( RatDecodeHeader(interp, envPtr->to->personal, 0), -1); break; } /* fallthrough */ case RAT_FOLDER_MAIL_RECIPIENT: oPtr = Tcl_NewObj(); adrPtr = envPtr->to; for (; adrPtr; adrPtr = adrPtr->next) { if (adrPtr->mailbox && adrPtr->host) { break; } } if (!adrPtr) { Tcl_DecrRefCount(oPtr); oPtr = Tcl_NewObj(); } else { Tcl_AppendToObj(oPtr, RatAddressMail(adrPtr), -1); } break; case RAT_FOLDER_SIZE: oPtr = Tcl_NewIntObj(size); break; case RAT_FOLDER_SIZE_F: oPtr = RatMangleNumber(size); break; case RAT_FOLDER_DATE_F: if (envPtr->date && T == mail_parse_date(&dateElt, envPtr->date)) { dateEltPtr = &dateElt; } else { dateEltPtr = eltPtr; } oPtr = RatFormatDate(interp, dateEltPtr->month-1, dateEltPtr->day); break; case RAT_FOLDER_DATE_N: if (envPtr->date && T == mail_parse_date(&dateElt, envPtr->date)) { dateEltPtr = &dateElt; } else { dateEltPtr = eltPtr; } tm.tm_sec = dateEltPtr->seconds; tm.tm_min = dateEltPtr->minutes; tm.tm_hour = dateEltPtr->hours; tm.tm_mday = dateEltPtr->day; tm.tm_mon = dateEltPtr->month - 1; tm.tm_year = dateEltPtr->year+70; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; time = mktime(&tm); zonediff = (dateEltPtr->zhours*60+dateEltPtr->zminutes)*60; if (!dateEltPtr->zoccident) { zonediff *= -1; } time += zonediff; oPtr = Tcl_NewObj(); Tcl_SetLongObj(oPtr, time); break; case RAT_FOLDER_DATE_IMAP4: if (envPtr->date && T == mail_parse_date(&dateElt, envPtr->date)) { dateEltPtr = &dateElt; } else { dateEltPtr = eltPtr; } mail_date(buf, dateEltPtr); oPtr = Tcl_NewStringObj(buf, -1); break; case RAT_FOLDER_TYPE: oPtr = Tcl_NewObj(); Tcl_AppendStringsToObj(oPtr, body_types[bodyPtr->type], "/", bodyPtr->subtype, NULL); break; case RAT_FOLDER_PARAMETERS: oPtr = Tcl_NewObj(); for (parmPtr = bodyPtr->parameter; parmPtr; parmPtr = parmPtr->next) { pPtr[0] = Tcl_NewStringObj(parmPtr->attribute, -1); pPtr[1] = Tcl_NewStringObj(parmPtr->value, -1); Tcl_ListObjAppendElement(interp, oPtr, Tcl_NewListObj(2,pPtr)); } break; case RAT_FOLDER_TO: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->to, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->to); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_FROM: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->from, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->from); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_SENDER: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->sender, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->sender); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_CC: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->cc, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->cc); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_REPLY_TO: oPtr = Tcl_NewStringObj("", 0); Tcl_SetObjLength(oPtr, RatAddressSize(envPtr->reply_to, 1)); Tcl_GetString(oPtr)[0] = '\0'; rfc822_write_address(Tcl_GetString(oPtr), envPtr->reply_to); Tcl_SetObjLength(oPtr, strlen(Tcl_GetString(oPtr))); break; case RAT_FOLDER_FLAGS: oPtr = Tcl_NewStringObj(MsgFlags(eltPtr), -1); break; case RAT_FOLDER_UNIXFLAGS: s = buf; if (eltPtr->seen) *s++ = 'R'; if (eltPtr->deleted) *s++ = 'D'; if (eltPtr->flagged) *s++ = 'F'; if (eltPtr->answered) *s++ = 'A'; oPtr = Tcl_NewStringObj(buf, s-buf); break; case RAT_FOLDER_MSGID: oPtr = RatExtractRef(envPtr->message_id); if (NULL == oPtr) { oPtr = Tcl_NewObj(); } break; case RAT_FOLDER_REF: oPtr = RatExtractRef(envPtr->in_reply_to); if (NULL == oPtr) { oPtr = RatExtractRef(envPtr->references); } if (NULL == oPtr) { oPtr = Tcl_NewObj(); } break; case RAT_FOLDER_STATUS: /*fallthrough */ case RAT_FOLDER_INDEX: /*fallthrough */ case RAT_FOLDER_THREADING: /*fallthrough */ case RAT_FOLDER_END: oPtr = Tcl_NewObj(); break; } msgPtr->info[type] = oPtr; Tcl_IncrRefCount(oPtr); return oPtr; } /* *---------------------------------------------------------------------- * * MsgFlags -- * * Returns the flags of a message * * Results: * A poiter to a static area containing the flags * * Side effects: * None * * *---------------------------------------------------------------------- */ char* MsgFlags(MESSAGECACHE *eltPtr) { static Tcl_DString ds; static int initialized = 0; if (!initialized) { Tcl_DStringInit(&ds); initialized = 1; } Tcl_DStringSetLength(&ds, 0); if (eltPtr->seen) { Tcl_DStringAppend(&ds, flag_name[RAT_SEEN].imap_name, -1); } if (eltPtr->deleted) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_DELETED].imap_name, -1); } if (eltPtr->flagged) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_FLAGGED].imap_name, -1); } if (eltPtr->answered) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_ANSWERED].imap_name, -1); } if (eltPtr->draft) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_DRAFT].imap_name, -1); } if (eltPtr->recent) { if (Tcl_DStringLength(&ds)) { Tcl_DStringAppend(&ds, " ",1); } Tcl_DStringAppend(&ds, flag_name[RAT_RECENT].imap_name, -1); } return Tcl_DStringValue(&ds); } /* *---------------------------------------------------------------------- * * RatParseFrom -- * * Parse the time in a 'From ' line. See ../imap/src/osdep/unix/unix.h * for details on how this line may look like. * * Results: * A poiter to a static area containing a MESSAGECACHE. * The only valid fields in this are the time-fields * * Side effects: * None * * *---------------------------------------------------------------------- */ MESSAGECACHE* RatParseFrom(const char *from) { static MESSAGECACHE elt; const char *cPtr; int i=0, found; /* * Start by finding the weekday name, if it is followed by one * space and a month-spec, then we assume we have found the date. */ for (cPtr = from+5, found=0; cPtr && !found; cPtr = strchr(cPtr, ' ')) { for (i=0; i<7 && strncmp(cPtr+1, dayName[i], 3); i++); if (i < 7) { for (i=0; i<12; i++) { if (!strncmp(cPtr+5, monthName[i], 3)) { found = 1; break; } } } } if (!found) { return NULL; } elt.month = i+1; for (cPtr+=8; isspace(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; elt.day = atoi(cPtr); for (cPtr++; !isspace(*cPtr) && *cPtr; cPtr++); for (cPtr++; isspace(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; elt.hours = atoi(cPtr); for (cPtr++; ':' != *cPtr && *cPtr; cPtr++); elt.minutes = atoi(cPtr+1); for (cPtr++; isdigit(*cPtr) && *cPtr; cPtr++); if (!*cPtr) return NULL; if (':' == *cPtr) { elt.seconds = atoi(cPtr+1); for (cPtr++; isdigit(*cPtr) && *cPtr; cPtr++); } else { elt.seconds = 0; } while (1) { for (cPtr++; isspace(*cPtr) && *cPtr; cPtr++); if (isdigit(cPtr[0]) && isdigit(cPtr[1]) && isdigit(cPtr[2]) && isdigit(cPtr[3])){ elt.year = atoi(cPtr)-BASEYEAR; break; } else { for (cPtr++; !isspace(*cPtr) && *cPtr; cPtr++); } if (!*cPtr) return NULL; } elt.zoccident = 0; elt.zhours = 0; elt.zminutes = 0; return &elt; } /* *---------------------------------------------------------------------- * * RatUpdateFolder -- * * Updates a folder * * Results: * The number of new messages is left in the tcl result-buffer. * A standard tcl-result is returned. * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatUpdateFolder(Tcl_Interp *interp, RatFolderInfo *infoPtr, RatUpdateType mode) { int i, numNew, oldNumber, oldVisible, delta; oldVisible = infoPtr->visible; oldNumber = infoPtr->number; numNew = (*infoPtr->updateProc)(infoPtr, interp, mode); if (numNew < 0) { return TCL_ERROR; } else if (numNew || oldNumber != infoPtr->number || infoPtr->sortOrderChanged) { if (infoPtr->number > infoPtr->allocated) { infoPtr->allocated = infoPtr->number; infoPtr->msgCmdPtr = (char **) ckrealloc(infoPtr->msgCmdPtr, infoPtr->allocated*sizeof(char*)); infoPtr->privatePtr = (ClientData**)ckrealloc(infoPtr->privatePtr, infoPtr->allocated*sizeof(ClientData*)); infoPtr->presentationOrder = (int *) ckrealloc( infoPtr->presentationOrder, infoPtr->allocated*sizeof(int)); infoPtr->hidden = (int *) ckrealloc(infoPtr->hidden, infoPtr->allocated*sizeof(int)); } for (i=infoPtr->number-numNew; inumber; i++) { infoPtr->msgCmdPtr[i] = (char *) NULL; infoPtr->privatePtr[i] = (ClientData*) NULL; (*infoPtr->initProc)(infoPtr, interp, i); } RatFolderSort(interp, infoPtr); infoPtr->sortOrderChanged = 0; } delta = infoPtr->visible - oldVisible; Tcl_SetObjResult(interp, Tcl_NewIntObj((delta>0 ? delta : 0))); if (delta) { Tcl_SetVar2Ex(interp, "folderExists", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->visible), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderRecent", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->recent), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderUnseen", infoPtr->cmdName, Tcl_NewIntObj(infoPtr->unseen), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "folderChanged", infoPtr->cmdName, Tcl_NewIntObj(++folderChangeId), TCL_GLOBAL_ONLY); } return TCL_OK; } /* *---------------------------------------------------------------------- * * RatFolderUpdateTime -- * * Updates a folder * * Results: * The number of new messages is left in the tcl result-buffer. * A standard tcl-result is returned. * * Side effects: * None * * *---------------------------------------------------------------------- */ static void RatFolderUpdateTime(ClientData clientData) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; RatSetBusy(timerInterp); if (TCL_OK == RatUpdateFolder(timerInterp, infoPtr, RAT_UPDATE)) { infoPtr->timerToken = Tcl_CreateTimerHandler( infoPtr->watcherInterval*1000, RatFolderUpdateTime, (ClientData)infoPtr); } else { char *err = cpystr(Tcl_GetStringResult(timerInterp)); RatLog(timerInterp, RAT_ERROR, err, RATLOG_EXPLICIT); ckfree(err); } RatClearBusy(timerInterp); } /* *---------------------------------------------------------------------- * * RatFolderClose -- * * Closes a folder * * Results: * A standard tcl result * * Side effects: * Many, all associated with cleaning up from the folder * * *---------------------------------------------------------------------- */ int RatFolderClose(Tcl_Interp *interp, RatFolderInfo *infoPtr, int force) { RatFolderInfo **rfiPtrPtr; int i, ret, expunge; Tcl_Obj *oPtr; oPtr = Tcl_GetVar2Ex(interp, "option", "expunge_on_close",TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &expunge); if (infoPtr->refCount-- != 1 && !force) { if (expunge) { RatUpdateFolder(interp, infoPtr, RAT_SYNC); } return TCL_OK; } for (rfiPtrPtr = &ratFolderList; infoPtr != *rfiPtrPtr; rfiPtrPtr = &(*rfiPtrPtr)->nextPtr); *rfiPtrPtr = infoPtr->nextPtr; ckfree(infoPtr->name); ckfree(infoPtr->definition); ret = (*infoPtr->closeProc)(infoPtr, interp, expunge); for(i=0; i < infoPtr->number; i++) { if (NULL != infoPtr->msgCmdPtr[i]) { (void)RatMessageDelete(interp, infoPtr->msgCmdPtr[i]); infoPtr->msgCmdPtr[i] = 0; } } if (infoPtr->watcherInterval) { Tcl_DeleteTimerHandler(infoPtr->timerToken); } Tcl_UnsetVar2(interp, "folderExists", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "folderUnseen", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "folderChanged", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, "vFolderWatch", infoPtr->cmdName,TCL_GLOBAL_ONLY); Tcl_UnsetVar(interp, infoPtr->cmdName, TCL_GLOBAL_ONLY); (void)Tcl_DeleteCommand(interp, infoPtr->cmdName); ckfree(infoPtr->cmdName); ckfree(infoPtr->msgCmdPtr); ckfree(infoPtr->privatePtr); ckfree(infoPtr->presentationOrder); ckfree(infoPtr->hidden); ckfree(infoPtr); return ret; } /* *---------------------------------------------------------------------- * * RatFolderInsert -- * * Insert messages into a folder * * Results: * A standard tcl result * * Side effects: * Messages gets added * * *---------------------------------------------------------------------- */ int RatFolderInsert(Tcl_Interp *interp, RatFolderInfo *infoPtr,int num,char **msgs) { int result; result = (*infoPtr->insertProc)(infoPtr, interp, num, msgs); RatUpdateFolder(interp, infoPtr, RAT_UPDATE); return result; } /* *---------------------------------------------------------------------- * * RatGetFolderSpec -- * * Return the mailbox spec for the given folder definition * * Results: * A pointer to a static area of memeory where the spec is stored. * This area will be overwritten by the next call. * * Side effects: * None * * *---------------------------------------------------------------------- */ char* RatGetFolderSpec(Tcl_Interp *interp, Tcl_Obj *def) { static Tcl_DString ds, tmpDS; static int initialized = 0; Tcl_Obj *oPtr, **objv, **fobjv, **mobjv, **sobjv; int objc, fobjc, mobjc, sobjc, port, i, j; char buf[64], *type, *file, *c; if (0 == initialized) { Tcl_DStringInit(&ds); } else { Tcl_DStringSetLength(&ds, 0); } Tcl_ListObjGetElements(interp, def, &objc, &objv); if (objc < 4) { return NULL; } type = Tcl_GetString(objv[1]); if (!strcmp(type, "file")) { file = Tcl_TranslateFileName(interp, Tcl_GetString(objv[3]), &tmpDS); if (NULL == file) { Tcl_DStringAppend(&ds, "invalid_file_specified", -1); } else { RatDecodeQP(file); Tcl_DStringAppend(&ds, file, -1); Tcl_DStringFree(&tmpDS); c = Tcl_GetString(objv[3]); if ('/' == c[strlen(c)-1]) { Tcl_DStringAppend(&ds, "/", 1); } } } else if (!strcmp(type, "mh")) { Tcl_DStringAppend(&ds, "#mh/", 4); file = cpystr(Tcl_GetString(objv[3])); RatDecodeQP(file); Tcl_DStringAppend(&ds, file, -1); ckfree(file); } else if (!strcmp(type, "dbase")) { if (objc < 6) { return NULL; } Tcl_DStringAppend(&ds, Tcl_GetString(objv[3]), -1); Tcl_DStringAppend(&ds, Tcl_GetString(objv[4]), -1); Tcl_DStringAppend(&ds, Tcl_GetString(objv[5]), -1); } else if (!strcmp(type, "imap") || !strcmp(type, "pop3") || !strcmp(type, "dis")) { oPtr = Tcl_GetVar2Ex(interp, "mailServer", Tcl_GetString(objv[3]), TCL_GLOBAL_ONLY); if (!oPtr) { return NULL; } Tcl_ListObjGetElements(interp, oPtr, &mobjc, &mobjv); Tcl_DStringAppend(&ds, "{", 1); Tcl_DStringAppend(&ds, Tcl_GetString(mobjv[0]), Tcl_GetCharLength(mobjv[0])); if (TCL_OK == Tcl_GetIntFromObj(interp, mobjv[1], &port) && port!=0) { snprintf(buf, sizeof(buf), ":%d", port); Tcl_DStringAppend(&ds, buf, -1); } if (!strcmp(type, "pop3")) { Tcl_DStringAppend(&ds, "/pop3", 5); } else { Tcl_DStringAppend(&ds, "/imap", 5); } Tcl_ListObjGetElements(interp, mobjv[2], &fobjc, &fobjv); #ifdef HAVE_OPENSSL /* * These flags must be in a specific order to match strings generated * by c-client. Also only include them if we have SSL available. */ for (i=0; cClientFlags[i]; i++) { for (j=0; j'))) { ls = s+1; le = e; } if (ls) { oPtr = Tcl_NewObj(); for (s=ls; s