/* * ratStdFolder.c -- * * This file contains code which implements standard c-client folders. * This means ONLY filefolders at this moment. * * 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 "ratStdFolder.h" /* * We use this structure to keep a list of open connections */ typedef struct Connection { MAILSTREAM *stream; /* Handler to c-client entity */ int *errorFlagPtr; /* Address of flag to set on hard errors */ int refcount; /* references count */ int closing; /* True if this connection is unused and waiting to be closed */ int isnet; /* Nonnull if this is a network conn */ Tcl_TimerToken token; /* Timer token for closing timer */ struct Connection *next; /* Struct linkage */ FolderHandlers *handlers; /* Event handlers */ } Connection; FolderHandlers **globHD; /* * Remember if we must initialize the package */ static int initialize = 1; /* * List of open connections */ static Connection *connListPtr = NULL; /* * The values below are used to catch calls to mm_log. That is when you * want to handle the message internally. */ static RatLogLevel logLevel; static char *logMessage = NULL; int logIgnore = 0; /* * These variables are used by mm_login */ static char loginPassword[MAILTMPLEN]; static char loginSpec[MAILTMPLEN]; static int loginStore; /* * This is used to build a list of found mailboxes when listing */ typedef struct Mailbox { char *name; /* The en component of name the mailbox name */ char *folder; /* The mailbox name */ long attributes; /* The attributes from c-client */ int delimiter; /* The delimiter in the folder names */ struct Mailbox *next; /* Pointer to the next mailbox on this level */ struct Mailbox *child; /* Pointer to subfolders */ } Mailbox; static Mailbox *mailboxListPtr = NULL; static char *mailboxSearchBase = NULL; static char lastDelimiter[2] = {'\0', '\0'}; /* * Used to store search results */ long *searchResultPtr = NULL; int searchResultSize = 0; int searchResultNum = 0; /* * Used to store status results */ MAILSTATUS stdStatus; /* * File handler of debugging file */ static FILE *debugFile = NULL; /* * Procedures private to this module. */ static RatInitProc Std_InitProc; static RatCloseProc Std_CloseProc; static RatUpdateProc Std_UpdateProc; static RatInsertProc Std_InsertProc; static RatSetFlagProc Std_SetFlagProc; static RatGetFlagProc Std_GetFlagProc; static Tcl_TimerProc CloseConnection; static Tcl_ObjCmdProc StdImportCmd; static Connection *FindConn(MAILSTREAM *stream); static void StdImportBuildResult(Tcl_Interp *interp, Mailbox *mPtr, int *lastId, int id, int templatec, Tcl_Obj **templatev); static HandleExists Std_HandleExists; static HandleExpunged Std_HandleExpunged; static RatStdFolderType Std_GetType(const char *spec); /* *---------------------------------------------------------------------- * * RatStdFolderInit -- * * Initializes the file folder command. * * 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 C-client library is initialized and the apropriate mail drivers * are linked. * * *---------------------------------------------------------------------- */ int RatStdFolderInit(Tcl_Interp *interp) { /* Link imap code */ #include "../imap/c-client/linkage.c" Tcl_CreateObjCommand(interp, "RatImport", StdImportCmd, NULL, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_StreamOpen -- * * Opens a standard c-client mailstream. This function handles * caching of passwords and connections. * * Results: * The mail stream. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ MAILSTREAM* Std_StreamOpen(Tcl_Interp *interp, char *spec, long options, int *errorFlagPtr, FolderHandlers *handlers) { MAILSTREAM *stream = NULL; Connection *connPtr = NULL; char *host = NULL, *cPtr; int len; if ('{' == spec[0]) { strlcpy(loginSpec, spec, sizeof(loginSpec)); cPtr = strchr(loginSpec, '}'); cPtr[1] = '\0'; len = strchr(spec, '}') - spec; if (NULL != (cPtr = strstr(spec, "/debug}"))) { len = cPtr-spec; } for (connPtr = connListPtr; connPtr; connPtr = connPtr->next) { if ((connPtr->closing || options & OP_HALFOPEN) && !strncmp(spec, connPtr->stream->mailbox, len)) { break; } } if (connPtr) { stream = connPtr->stream; connPtr->refcount++; Tcl_DeleteTimerHandler(connPtr->token); if (connPtr->closing) { connPtr->handlers = handlers; connPtr->errorFlagPtr = errorFlagPtr; } connPtr->closing = 0; } } if (stream && options & OP_HALFOPEN) { ckfree(host); return stream; } loginPassword[0] = '\0'; stream = mail_open(stream, spec, options); if (stream && !connPtr) { connPtr = (Connection*)ckalloc(sizeof(Connection)); connPtr->stream = stream; connPtr->errorFlagPtr = errorFlagPtr; connPtr->refcount = 1; connPtr->closing = 0; connPtr->handlers = handlers; connPtr->next = connListPtr; connPtr->token = NULL; connPtr->isnet = (('{' == spec[0]) ? 1 : 0); connListPtr = connPtr; if (loginPassword[0] != '\0') { RatCachePassword(interp, spec, loginPassword, loginStore); memset(loginPassword, 0, strlen(loginPassword)); } } if (!stream && '{' == spec[0]) { Tcl_Obj *oPtr; int n; oPtr = Tcl_GetVar2Ex(interp, "ratNetOpenFailures", NULL, TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &n); Tcl_SetVar2Ex(interp, "ratNetOpenFailures", NULL, Tcl_NewIntObj(++n), TCL_GLOBAL_ONLY); } if (errorFlagPtr) { *errorFlagPtr = 0; } ckfree(host); return stream; } /* *---------------------------------------------------------------------- * * Std_StreamClose -- * * Closes a standard c-client mailstream. This function handles * caching of passwords and connections. * * Results: * None. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ void Std_StreamClose(Tcl_Interp *interp, MAILSTREAM *stream) { Connection *connPtr; Tcl_Obj *oPtr; for (connPtr = connListPtr; connPtr && stream != connPtr->stream; connPtr = connPtr->next); if (connPtr) { int timeout, doCache; if (--connPtr->refcount) { return; } oPtr = Tcl_GetVar2Ex(interp, "option", "cache_conn", TCL_GLOBAL_ONLY); Tcl_GetBooleanFromObj(interp, oPtr, &doCache); if (doCache && RAT_IMAP == Std_GetType(connPtr->stream->mailbox) && (!connPtr->errorFlagPtr || 0 == *connPtr->errorFlagPtr)) { oPtr = Tcl_GetVar2Ex(interp, "option", "cache_conn_timeout", TCL_GLOBAL_ONLY); Tcl_GetIntFromObj(interp, oPtr, &timeout); connPtr->closing = 1; if (connPtr->errorFlagPtr) { connPtr->errorFlagPtr = NULL; } if (timeout) { connPtr->token = Tcl_CreateTimerHandler(timeout*1000, CloseConnection, (ClientData)connPtr); } else { connPtr->token = NULL; } } else { CloseConnection((ClientData)connPtr); } } else { logIgnore++; mail_close_full(stream, NIL); logIgnore--; } } /* *---------------------------------------------------------------------- * * Std_StreamCloseAllCached -- * * Forces a close of all cached connections * * Results: * None. * * Side effects: * The caches may be modified. * *---------------------------------------------------------------------- */ void Std_StreamCloseAllCached(Tcl_Interp *interp) { Connection *connPtr, *nextPtr; for (connPtr = connListPtr; connPtr; connPtr = nextPtr) { nextPtr = connPtr->next; if (connPtr->closing) { Tcl_DeleteTimerHandler(connPtr->token); CloseConnection((ClientData)connPtr); } } } /* *---------------------------------------------------------------------- * * OpenStdFolder -- * * Opens a standard c-client folder and if it is a filefolder and * is of an incompatible format (unfortunately generated by an older * version of this program) we convert it. * * Results: * The mail stream. * * Side effects: * None. * * *---------------------------------------------------------------------- */ MAILSTREAM* OpenStdFolder(Tcl_Interp *interp, char *spec, void *voidPtr) { MAILSTREAM *stream = NULL; RatStdFolderType type; StdFolderInfo *stdPtr = (StdFolderInfo*)voidPtr; Tcl_DString dsBuf; struct stat sbuf; int dsBufUse = 0; type = Std_GetType(spec); if (RAT_UNIX == type) { spec = Tcl_UtfToExternalDString(NULL, spec, -1, &dsBuf); dsBufUse = 1; } if ('/' == spec[0] && stat(spec, &sbuf) && ENOENT == errno) { int fd; fd = open(spec, O_CREAT | O_WRONLY, 0600); close(fd); } logLevel = RAT_BABBLE; stream = Std_StreamOpen(interp, spec, 0, (stdPtr ? &stdPtr->error : NULL), (stdPtr ? &stdPtr->handlers : NULL)); if (logLevel > RAT_WARN) { Tcl_SetResult(interp, logMessage, TCL_VOLATILE); return NULL; } if (NIL == stream) { Tcl_AppendResult(interp, "Failed to open std mailbox \"", spec, "\"", (char *) NULL); return NULL; } if (!strcmp(stream->dtb->name, "mbx")) { type = RAT_MBX; } if (stdPtr) { stdPtr->stream = stream; stdPtr->referenceCount = 1; stdPtr->exists = stream->nmsgs; stdPtr->type = type; } if (dsBufUse) { Tcl_DStringFree(&dsBuf); } return stream; } /* *---------------------------------------------------------------------- * * RatStdFolderCreate -- * * Creates a std folder entity. * * 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: * A std folder is created. * * *---------------------------------------------------------------------- */ RatFolderInfo* RatStdFolderCreate(Tcl_Interp *interp, Tcl_Obj *defPtr) { RatFolderInfo *infoPtr; StdFolderInfo *stdPtr; MAILSTREAM *stream = NULL; char buf[32]; Tcl_Obj *oPtr; char *spec; int i; /* * Now it is time to initialize things */ if (initialize) { char *role, *domain; role = Tcl_GetVar2(interp, "option", "default_role",TCL_GLOBAL_ONLY); domain = RatGetCurrent(interp, RAT_HOST, role); env_parameters(SET_LOCALHOST, (void*)domain); initialize = 0; } stdPtr = (StdFolderInfo *) ckalloc(sizeof(*stdPtr)); stdPtr->handlers.state = (void*)stdPtr; stdPtr->handlers.exists = Std_HandleExists; stdPtr->handlers.expunged = Std_HandleExpunged; if (NULL == (spec = RatGetFolderSpec(interp, defPtr)) || NULL == (stream = OpenStdFolder(interp, spec, stdPtr))) { ckfree(stdPtr); return NULL; } infoPtr = (RatFolderInfo *) ckalloc(sizeof(*infoPtr)); infoPtr->type = "std"; Tcl_ListObjIndex(interp, defPtr, 0, &oPtr); infoPtr->name = cpystr(Tcl_GetString(oPtr)); infoPtr->size = -1; infoPtr->number = stream->nmsgs; infoPtr->recent = stream->recent; infoPtr->unseen = 0; if (stream->nmsgs) { sprintf(buf, "1:%ld", stream->nmsgs); mail_fetchfast_full(stream, buf, NIL); for (i = 1; i <= stream->nmsgs; i++) if (!mail_elt (stream,i)->seen) infoPtr->unseen++; } infoPtr->initProc = Std_InitProc; infoPtr->finalProc = NULL; infoPtr->closeProc = Std_CloseProc; infoPtr->updateProc = Std_UpdateProc; infoPtr->insertProc = Std_InsertProc; infoPtr->setFlagProc = Std_SetFlagProc; infoPtr->getFlagProc = Std_GetFlagProc; infoPtr->infoProc = Std_InfoProc; infoPtr->setInfoProc = Std_SetInfoProc; infoPtr->createProc = Std_CreateProc; infoPtr->syncProc = NULL; infoPtr->private = (ClientData) stdPtr; return infoPtr; } /* *---------------------------------------------------------------------- * * Std_InitProc -- * * See the documentation for initProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for initProc in ratFolder.h * * *---------------------------------------------------------------------- */ static void Std_InitProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr; int i, j, start, end; if (-1 == index) { start = 0; end = infoPtr->number; } else { start = index; end = start+1; } for (i=start; ifolderInfoPtr = infoPtr; msgPtr->name[0] = '\0'; msgPtr->type = RAT_CCLIENT_MESSAGE; msgPtr->bodyInfoPtr = NULL; msgPtr->msgNo = i; msgPtr->fromMe = RAT_ISME_UNKOWN; msgPtr->toMe = RAT_ISME_UNKOWN; msgPtr->clientData = NULL; for (j=0; jinfo[j] = NULL; } infoPtr->privatePtr[i] = (ClientData)msgPtr; } RatStdMsgStructInit(infoPtr, interp, index, stdPtr->stream, stdPtr->type); } /* *---------------------------------------------------------------------- * * CloseStdFolder -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ void CloseStdFolder(Tcl_Interp *interp, MAILSTREAM *stream) { Std_StreamClose(interp, stream); } /* *---------------------------------------------------------------------- * * Std_CloseProc -- * * See the documentation for closeProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for closeProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_CloseProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int expunge) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr; int i, j; if (stdPtr->stream) { if (expunge) { logIgnore++; mail_expunge(stdPtr->stream); logIgnore--; } Std_StreamClose(interp, stdPtr->stream); } if (0 == --stdPtr->referenceCount) { for (i=0; inumber; i++) { if (NULL == infoPtr->msgCmdPtr[i]) { msgPtr = (MessageInfo*)infoPtr->privatePtr[i]; if (msgPtr) { for (j=0; jinfo[j]) { Tcl_DecrRefCount(msgPtr->info[j]); msgPtr->info[j] = NULL; } } ckfree(msgPtr->clientData); ckfree(infoPtr->privatePtr[i]); } } } ckfree(stdPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_UpdateProc -- * * See the documentation for updateProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for updateProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_UpdateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp,RatUpdateType mode) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; int numNew = 0, oldExists, i; char sequence[16]; if (RAT_SYNC == mode) { MESSAGECACHE *cachePtr; MessageInfo *msgPtr; int i, offset = 0; if (infoPtr->number) { for (i=0; inumber; i++) { cachePtr = mail_elt(stdPtr->stream, i+1); if (cachePtr->deleted) { if (-1 != infoPtr->size) { infoPtr->size -= cachePtr->rfc822_size; } if (infoPtr->msgCmdPtr[i]) { RatMessageDelete(interp, infoPtr->msgCmdPtr[i]); } offset++; } else if (offset) { infoPtr->msgCmdPtr[i-offset] = infoPtr->msgCmdPtr[i]; infoPtr->privatePtr[i-offset] = infoPtr->privatePtr[i]; if (infoPtr->privatePtr[i]) { msgPtr = (MessageInfo*)infoPtr->privatePtr[i]; msgPtr->msgNo = i - offset; } } } for (i=infoPtr->number-offset; inumber; i++) { infoPtr->msgCmdPtr[i] = NULL; infoPtr->privatePtr[i] = NULL; } } mail_expunge(stdPtr->stream); numNew = stdPtr->exists - (infoPtr->number - offset); } else if (RAT_CHECKPOINT == mode) { oldExists = infoPtr->number; mail_check(stdPtr->stream); numNew = stdPtr->exists-oldExists; } else { oldExists = infoPtr->number; if (T != mail_ping(stdPtr->stream)) { char buf[1024]; stdPtr->stream = NIL; snprintf(buf, sizeof(buf), "%s close 1", infoPtr->cmdName); Tcl_GlobalEval(interp, buf); Tcl_SetResult(interp, "Lost contact with mailbox", TCL_STATIC); Tcl_SetErrorCode(interp, "C_CLIENT", "streamdied", NULL); return -1; } numNew = stdPtr->exists-oldExists; } if (numNew) { sprintf(sequence, "%d:%d", stdPtr->exists-numNew+1, stdPtr->exists); mail_fetchfast_full(stdPtr->stream, sequence, NIL); } infoPtr->number = stdPtr->exists; infoPtr->recent = stdPtr->stream->recent; for (i = 1,infoPtr->unseen=0; i <= stdPtr->stream->nmsgs; i++) { if (!mail_elt(stdPtr->stream,i)->seen) infoPtr->unseen++; } return numNew; } /* *---------------------------------------------------------------------- * * Std_InsertProc -- * * See the documentation for insertProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for insertProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_InsertProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int argc, char *argv[]) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; char flags[128], date[128]; Tcl_CmdInfo cmdInfo; Tcl_DString ds; STRING string; int i; if (NIL == stdPtr->stream) { Tcl_AppendResult(interp, "Failed to open std mailbox \"", argv[2], "\"", (char *) NULL); return TCL_ERROR; } Tcl_DStringInit(&ds); for (i=0; istream, stdPtr->stream->mailbox, flags, date, &string)){ Tcl_SetResult(interp, "mail_append failed", TCL_STATIC); return TCL_ERROR; } Tcl_DStringSetLength(&ds, 0); if (!stdPtr->exists) { if (T != mail_ping(stdPtr->stream)) { char buf[1024]; Tcl_DStringFree(&ds); snprintf(buf, sizeof(buf), "%s close", infoPtr->cmdName); Tcl_GlobalEval(interp, buf); Tcl_SetResult(interp, "Mailbox stream died", TCL_STATIC); Tcl_SetErrorCode(interp, "C_CLIENT", "streamdied", NULL); return TCL_ERROR; } } } Tcl_DStringFree(&ds); return TCL_OK; } /* *---------------------------------------------------------------------- * * Std_SetFlagProc -- * * See the documentation for setFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_SetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag, int value) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MessageInfo *msgPtr = (MessageInfo*)infoPtr->privatePtr[index]; MESSAGECACHE *cachePtr; char sequence[8]; int wasseen; if (!stdPtr->stream || stdPtr->stream->rdonly) { return TCL_OK; } if (msgPtr->info[RAT_FOLDER_STATUS]) { Tcl_DecrRefCount(msgPtr->info[RAT_FOLDER_STATUS]); msgPtr->info[RAT_FOLDER_STATUS] = NULL; } cachePtr = mail_elt(stdPtr->stream, index+1); wasseen = cachePtr->seen; sprintf(sequence, "%d", index+1); if (value) { mail_setflag_full(stdPtr->stream, sequence, flag_name[flag].imap_name, NIL); } else { mail_clearflag_full(stdPtr->stream, sequence, flag_name[flag].imap_name, NIL); } (void)mail_fetchenvelope(stdPtr->stream, index+1); cachePtr = mail_elt(stdPtr->stream, index+1); switch (flag) { case RAT_SEEN: if (wasseen != value) { if (wasseen) { infoPtr->unseen++; } else { infoPtr->unseen--; } } cachePtr->seen = value; break; break; case RAT_DELETED: cachePtr->deleted = value; break; case RAT_FLAGGED: cachePtr->flagged = value; break; case RAT_ANSWERED: cachePtr->answered = value; break; case RAT_DRAFT: cachePtr->draft = value; break; case RAT_RECENT: cachePtr->recent = value; break; } infoPtr->recent = stdPtr->stream->recent; if (logLevel > RAT_WARN) { Tcl_SetResult(interp, logMessage, TCL_VOLATILE); return TCL_ERROR; } else { return TCL_OK; } } /* *---------------------------------------------------------------------- * * Std_GetFlagProc -- * * See the documentation for getFlagProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for getFlagProc in ratFolder.h * * *---------------------------------------------------------------------- */ static int Std_GetFlagProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index, RatFlag flag) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; MESSAGECACHE *cachePtr; char sequence[8]; int value = 0; if (!stdPtr->stream) return 0; sprintf(sequence, "%d", index+1); logLevel = RAT_BABBLE; (void)mail_fetchstructure_full(stdPtr->stream, index+1, NIL, NIL); cachePtr = mail_elt(stdPtr->stream, index+1); switch (flag) { case RAT_SEEN: value = cachePtr->seen; break; case RAT_DELETED: value = cachePtr->deleted; break; case RAT_FLAGGED: value = cachePtr->flagged; break; case RAT_ANSWERED: value = cachePtr->answered; break; case RAT_DRAFT: value = cachePtr->draft; break; case RAT_RECENT: value = cachePtr->recent; break; } return value; } /* *---------------------------------------------------------------------- * * Std_InfoProc -- * * See the documentation for infoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for infoProc in ratFolder.h * * *---------------------------------------------------------------------- */ Tcl_Obj* Std_InfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; return Std_GetInfoProc(interp, (ClientData)infoPtr->privatePtr[index], type, 0); } /* *---------------------------------------------------------------------- * * Std_SetInfoProc -- * * See the documentation for setInfoProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for setInfoProc in ratFolder.h * * *---------------------------------------------------------------------- */ void Std_SetInfoProc(Tcl_Interp *interp, ClientData clientData, RatFolderInfoType type, int index, Tcl_Obj *oPtr) { RatFolderInfo *infoPtr = (RatFolderInfo*)clientData; MessageInfo *msgPtr = (MessageInfo*)infoPtr->privatePtr[index]; if (msgPtr->info[type]) { Tcl_DecrRefCount(msgPtr->info[type]); } msgPtr->info[type] = oPtr; if (oPtr) { Tcl_IncrRefCount(oPtr); } } /* *---------------------------------------------------------------------- * * Std_CreateProc -- * * See the documentation for createProc in ratFolder.h * * Results: * A standard Tcl result. * * Side effects: * See the documentation for createProc in ratFolder.h * * *---------------------------------------------------------------------- */ char* Std_CreateProc(RatFolderInfoPtr infoPtr, Tcl_Interp *interp, int index) { StdFolderInfo *stdPtr = (StdFolderInfo *) infoPtr->private; return RatStdMessageCreate(interp, infoPtr, stdPtr->stream, index); } /* *---------------------------------------------------------------------- * * StdImportCmd -- * * Import folders (via mm_list) * * Results: * The folders found are returned as a list * * Side effects: * RatLogin may be called. * * *---------------------------------------------------------------------- */ static int StdImportCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Obj *oPtr, **iobjv, **bobjv, *origPtr; int iobjc, bobjc, subscribed, i, lastId, id; char *spec, path[1024], buf[1024]; MAILSTREAM *stream = NULL; /* * Check arguments * check that we got one * check that it is is a folder definition id and that it * points to a correct import-folder. */ if (objc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " id\"", (char *) NULL); return TCL_ERROR; } origPtr = Tcl_GetVar2Ex(interp, "vFolderDef", Tcl_GetString(objv[1]), TCL_GLOBAL_ONLY); if (TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id) || NULL == origPtr || TCL_OK != Tcl_ListObjGetElements(interp, origPtr, &iobjc, &iobjv) || 6 != iobjc || strcmp("import", Tcl_GetString(iobjv[1])) || TCL_OK != Tcl_ListObjGetElements(interp, iobjv[3], &bobjc, &bobjv)){ Tcl_AppendResult(interp, "Bad folder id specified \"", Tcl_GetString(objv[1]), "\"", (char *) NULL); return TCL_ERROR; } spec = RatGetFolderSpec(interp, iobjv[3]); logIgnore++; stream = Std_StreamOpen(interp, spec, OP_HALFOPEN, NULL, NULL); logIgnore--; /* * See if we only want subscribed folders */ Tcl_ListObjLength(interp, iobjv[2], &i); for (subscribed = 0, i -= 2; i>=0; i -= 2) { Tcl_ListObjIndex(interp, iobjv[2], i, &oPtr); if (!strcmp("subscribed", Tcl_GetString(oPtr))) { Tcl_ListObjIndex(interp, iobjv[2], i+1, &oPtr); Tcl_GetIntFromObj(interp, oPtr, &subscribed); break; } } /* * Run search * This builds a list of all found folders in mailboxListPtr * First we run a dummy-search to get the hierarchy delimiter */ if ((mailboxSearchBase = strchr(spec, '}'))) { mailboxSearchBase++; } else { mailboxSearchBase = spec; } mail_list(stream, "", spec); strlcpy(buf, spec, sizeof(buf)); if (*mailboxSearchBase && lastDelimiter[0] != mailboxSearchBase[strlen(mailboxSearchBase)-1] && lastDelimiter[0] != Tcl_GetString(iobjv[4])[0]) { strlcat(buf, lastDelimiter, sizeof(buf)); } strlcat(buf, Tcl_GetString(iobjv[4]), sizeof(buf)); if (subscribed) { mail_lsub(stream, "", buf); } else { mail_list(stream, "", buf); } if (stream) { Std_StreamClose(interp, stream); } /* * Compare list in mailboxListPtr with already existing list */ if ('{' == spec[0]) { strlcpy(path, strchr(spec, '}')+1, sizeof(path)); } else { strlcpy(path, spec, sizeof(path)); } if ('*' == path[strlen(path)-1] || '%' == path[strlen(path)-1]) { path[strlen(path)-1] = '\0'; } if (mailboxListPtr && path[strlen(path)-1] == mailboxListPtr->delimiter) { path[strlen(path)-1] = '\0'; } snprintf(buf, sizeof(buf), "lindex [lsort -integer [array names vFolderDef]] end"); Tcl_GlobalEval(interp, buf); lastId = atoi(Tcl_GetStringResult(interp)); StdImportBuildResult(interp, mailboxListPtr, &lastId, id, bobjc, bobjv); mailboxListPtr = NULL; return TCL_OK; } /* *---------------------------------------------------------------------- * * StdImportBuildResult -- * * Recursive function which parses the import result into folders * * Results: * None * * Side effects: * The vFolderDef array may be modified * * *---------------------------------------------------------------------- */ typedef struct { int id; int objc; Tcl_Obj **objv; } Mbox; static void StdImportBuildResult(Tcl_Interp *interp, Mailbox *mPtr, int *lastId, int id, int templatec, Tcl_Obj **templatev) { Tcl_Obj **objv, *oPtr, *lPtr, *iPtr, *idList; Mailbox *nPtr; Mbox *mbox_in, *mbox_out = NULL; int i, num_mbox_in, mbox_out_alloc = 0, count, changed = 0, disconnected = 0, listpos; char buf[32]; if (!strcmp("dis", Tcl_GetString(templatev[1]))) { disconnected = 1; } /* * Split list of ids and create list of definitions */ snprintf(buf, sizeof(buf), "%d", id); oPtr = Tcl_GetVar2Ex(interp, "vFolderDef", buf, TCL_GLOBAL_ONLY); Tcl_ListObjIndex(interp, oPtr, 1, &iPtr); if (!strcmp("struct", Tcl_GetString(iPtr))) { listpos = 3; } else { listpos = 5; } Tcl_ListObjIndex(interp, oPtr, listpos, &idList); Tcl_ListObjGetElements(interp, idList, &num_mbox_in, &objv); mbox_in = (Mbox*)ckalloc(sizeof(Mbox)*num_mbox_in); for (i=0; i= mbox_out_alloc) { mbox_out_alloc += 100; mbox_out = (Mbox*)ckrealloc(mbox_out, mbox_out_alloc*sizeof(Mbox)); } for (i=0; iname, Tcl_GetString(mbox_in[i].objv[0])) && ((!strcmp("struct", Tcl_GetString(mbox_in[i].objv[1])) && 0 == (LATT_NOINFERIORS & mPtr->attributes)) || ((strcmp("struct", Tcl_GetString(mbox_in[i].objv[1])) && 0 != (LATT_NOINFERIORS & mPtr->attributes))))) { break; } } if (i == num_mbox_in) { /* Not found => new */ changed = 1; if (0 == (mPtr->attributes & LATT_NOSELECT)) { /* Create ordinary folder */ mbox_out[count].id = ++(*lastId); lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->name, -1)); Tcl_ListObjAppendElement(interp, lPtr, templatev[1]); Tcl_ListObjAppendElement(interp, lPtr, templatev[2]); if (5 == templatec) { Tcl_ListObjAppendElement(interp, lPtr, templatev[3]); } Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->folder, -1)); snprintf(buf, sizeof(buf), "%d", mbox_out[count].id); Tcl_SetVar2Ex(interp, "vFolderDef", buf, lPtr,TCL_GLOBAL_ONLY); Tcl_ListObjGetElements(interp, lPtr, &mbox_out[count].objc, &mbox_out[count].objv); if (disconnected) { RatDisManageFolder(interp, RAT_MGMT_CREATE, lPtr); } count++; } if (0 == (mPtr->attributes & LATT_NOINFERIORS)) { /* Create struct */ mbox_out[count].id = ++(*lastId); lPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj(mPtr->name, -1)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewStringObj("struct", 6)); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); Tcl_ListObjAppendElement(interp, lPtr, Tcl_NewObj()); snprintf(buf, sizeof(buf), "%d", mbox_out[count].id); Tcl_SetVar2Ex(interp, "vFolderDef", buf, lPtr,TCL_GLOBAL_ONLY); Tcl_ListObjGetElements(interp, lPtr, &mbox_out[count].objc, &mbox_out[count].objv); count++; } } else { /* Found => old */ mbox_out[count].id = mbox_in[i].id; mbox_out[count].objc = mbox_in[i].objc; mbox_out[count].objv = mbox_in[i].objv; mbox_in[i].id = 0; count++; } if (mPtr->child) { StdImportBuildResult(interp, mPtr->child, lastId, mbox_out[count-1].id, templatec, templatev); } nPtr = mPtr->next; ckfree(mPtr); } for (i=0; itoken); logIgnore++; mail_close_full(connPtr->stream, NIL); logIgnore--; for (connPtrPtr = &connListPtr; *connPtrPtr != connPtr; connPtrPtr = &(*connPtrPtr)->next); *connPtrPtr = connPtr->next; ckfree(connPtr); } /* *---------------------------------------------------------------------- * * AppendToIMAP -- * * Append the given message to an IMAP folder * * Results: * None. * * Side effects: * The specified folder will be modified * * *---------------------------------------------------------------------- */ void AppendToIMAP(Tcl_Interp *interp, const char *mailboxSpec, const char *flags, const char *date, const char *msg, int length) { char *mailbox; MAILSTREAM *stream; STRING msgString; int error; mailbox = RatLindex(interp, mailboxSpec, 0); if (NULL == (stream = Std_StreamOpen(interp, mailbox, 0, &error, NULL))) { return; } INIT(&msgString, mail_string, (char*)msg, length); mail_append_full(stream, (char*)mailbox, (char*)flags, (char*)date, &msgString); Std_StreamClose(interp, stream); } /* *---------------------------------------------------------------------- * * FindConn -- * * Find the connection pointer for the stream * * Results: * A connection pointer (or NULL) * * Side effects: * None * * *---------------------------------------------------------------------- */ static Connection* FindConn(MAILSTREAM *stream) { Connection *connPtr; for (connPtr=connListPtr; connPtr && connPtr->stream != stream; connPtr=connPtr->next); return connPtr; } /* *---------------------------------------------------------------------- * * Handle* -- * * Handle events from mailbox * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ static void Std_HandleExists(void *state, unsigned long nmsg) { StdFolderInfo *stdPtr = (StdFolderInfo *) state; stdPtr->exists = nmsg; } static void Std_HandleExpunged(void *state, unsigned long index) { StdFolderInfo *stdPtr = (StdFolderInfo *) state; stdPtr->exists--; } /* *---------------------------------------------------------------------- * * Std_GetType -- * * Determines the type of folder from a mailbox stream name * * Results: * The type * * Side effects: * None * * *---------------------------------------------------------------------- */ static RatStdFolderType Std_GetType(const char *spec) { const char *c; RatStdFolderType type; if ('{' == spec[0]) { type = RAT_IMAP; for (c=spec+1; *c != '}'; c++) { if ('/' == c[0] && 'p' == c[1] && 'o' == c[2] && 'p' == c[3] && '3' == c[4]) { type = RAT_POP; break; } } } else if ('#' == spec[0] && 'm' == spec[1] && 'h' == spec[2]) { type = RAT_MH; } else { type = RAT_UNIX; } return type; } /* *---------------------------------------------------------------------- * * RatStdManageFolder -- * * Create or delete folders on disk ro remote server * * Results: * A standard tcl result * * Side effects: * None * * *---------------------------------------------------------------------- */ int RatStdManageFolder(Tcl_Interp *interp, RatManagementAction op, Tcl_Obj *fPtr) { MAILSTREAM *stream; struct stat sbuf; char *spec; Tcl_Obj *oPtr; int result, error; spec = RatGetFolderSpec(interp, fPtr); if ('{' == spec[0]) { stream = Std_StreamOpen(interp, spec, OP_HALFOPEN, &error, NULL); if (!stream) { Tcl_SetResult(interp,"Failed to open stream to server",TCL_STATIC); return TCL_ERROR; } } else { stream = NULL; } if (op == RAT_MGMT_CREATE) { if ('/' == spec[0]) { /* * Since this is a file folder we check if the file already * exists, and do nothing if that is the case. This is to * avoid getting an error message */ if (0 == stat(spec, &sbuf)) { return TCL_OK; } } result = mail_create(stream, spec); } else { logIgnore++; (void)mail_delete(stream, spec); logIgnore--; result = 1; } if (stream) { Std_StreamClose(interp, stream); } Tcl_ListObjIndex(interp, fPtr, 1, &oPtr); if (result && !strcmp("dis", Tcl_GetString(oPtr))) { RatDisManageFolder(interp, op, fPtr); } if (result) { return TCL_OK; } else { Tcl_SetResult(interp, "Failed to create folder", TCL_STATIC); return TCL_ERROR; } } /* *---------------------------------------------------------------------- * * RatStdCheckNet -- * * Check if we have any network connections which are active * and if not go offline. * * Results: * None * * Side effects: * None * * *---------------------------------------------------------------------- */ void RatStdCheckNet(Tcl_Interp *interp) { Connection *connPtr; char buf[64]; int existsnetok = 0; for (connPtr = connListPtr; connPtr; connPtr = connPtr->next) { if (connPtr->isnet && (!connPtr->errorFlagPtr || 0 == *connPtr->errorFlagPtr)) { existsnetok = 1; } } if (0 == existsnetok) { strlcpy(buf, "SetOnlineStatus 0", sizeof(buf)); Tcl_Eval(interp, buf); } } /* *---------------------------------------------------------------------- * * mm_* * * The functions below are called from the C-client library. They * are docuemnted in Internal.DOC. * *---------------------------------------------------------------------- */ void mm_searched (MAILSTREAM *stream,unsigned long number) { if (searchResultSize == searchResultNum) { searchResultSize += 1024; searchResultPtr = (long*)ckrealloc(searchResultPtr, searchResultSize*sizeof(long)); } searchResultPtr[searchResultNum++] = number; } void mm_exists (MAILSTREAM *stream, unsigned long nmsgs) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->handlers && connPtr->handlers->exists) { (*connPtr->handlers->exists)(connPtr->handlers->state, nmsgs); } } void mm_expunged (MAILSTREAM *stream, unsigned long index) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->handlers && connPtr->handlers->expunged) { (*connPtr->handlers->expunged)(connPtr->handlers->state, index); } } void mm_mailbox (char *string) { } void mm_bboard (char *string) { } void mm_notify (MAILSTREAM *stream,char *string,long errflg) { if (errflg == BYE) { Connection *connPtr = FindConn(stream); if (connPtr && connPtr->errorFlagPtr) { *connPtr->errorFlagPtr = 1; } } } void mm_log (char *string,long errflg) { switch(errflg) { case NIL: logLevel = RAT_BABBLE; break; case PARSE: logLevel = RAT_PARSE; break; case WARN: logLevel = RAT_WARN; break; case BYE: logLevel = RAT_FATAL; break; case ERROR: /* fallthrough */ default: logLevel = RAT_ERROR; break; } ckfree(logMessage); logMessage = cpystr(string); if (logIgnore) { return; } RatLog(timerInterp, logLevel, string, RATLOG_NOWAIT); } void mm_dlog (char *string) { CONST84 char *filename; if (!debugFile && NULL != (filename = RatGetPathOption(timerInterp, "debug_file"))) { debugFile = fopen(filename, "a"); if (debugFile) { fchmod(fileno(debugFile), 0600); } } if (debugFile) { fprintf(debugFile, "%s\n", string); fflush(debugFile); } RatLog(timerInterp, RAT_BABBLE, string, RATLOG_TIME); } void mm_login (NETMBX *mbPtr, char *user, char *pwd, long trial) { char *pw; int objc; Tcl_Obj *oPtr, **objv; /* * Check for cached entry */ if ((pw = RatGetCachedPassword(timerInterp, loginSpec))) { strlcpy(user, mbPtr->user, MAILTMPLEN); strlcpy(pwd, pw, MAILTMPLEN); return; } oPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj("RatLogin", -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->host, -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewLongObj(trial)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->user, -1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewStringObj(mbPtr->service,-1)); Tcl_ListObjAppendElement(timerInterp, oPtr, Tcl_NewLongObj(mbPtr->port)); if (TCL_OK != Tcl_EvalObj(timerInterp, oPtr) || NULL == (oPtr = Tcl_GetObjResult(timerInterp)) || TCL_OK != Tcl_ListObjGetElements(timerInterp, oPtr, &objc, &objv) || 3 != objc) { pwd[0] = '\0'; return; } strlcpy(user, Tcl_GetString(objv[0]), MAILTMPLEN); strlcpy(pwd, Tcl_GetString(objv[1]), MAILTMPLEN); if ('\0' != user[0]) { strlcpy(loginPassword, Tcl_GetString(objv[1]), MAILTMPLEN); Tcl_GetBooleanFromObj(timerInterp, objv[2], &loginStore); } else { /* User pressed cancel */ loginStore = 0; logIgnore++; } } void mm_critical (MAILSTREAM *stream) { } void mm_nocritical (MAILSTREAM *stream) { } long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { char buf[64]; sprintf(buf, "Disk error: %ld", errcode); RatLog(timerInterp, RAT_FATAL, buf, RATLOG_TIME); return 1; } void mm_fatal (char *string) { RatLog(timerInterp, RAT_FATAL, string, RATLOG_TIME); } void mm_flags (MAILSTREAM *stream,unsigned long number) { } void mm_list(MAILSTREAM *stream, int delimiter, char *spec, long attributes) { Mailbox **mPtrPtr = &mailboxListPtr, *nPtr; char *name, *folder, *s, *e; int do_decode = 0; Tcl_DString *encoded; lastDelimiter[0] = delimiter; if ('{' == spec[0]) { for (s=spec; *s && 0 == (*s & 0x80); s++); if (!*s) { do_decode = 1; } } /* * Create new Mailbox structure */ if ((folder = strchr(spec, '}'))) { folder++; } else { folder = spec; } if (delimiter && (NULL != (name = strrchr(folder, delimiter)))) { name++; } else { name = folder; } if (!*name && !(attributes & LATT_NOSELECT)) { return; } /* * First find the right level */ if (!strncmp(mailboxSearchBase, folder, strlen(mailboxSearchBase))) { s = folder+strlen(mailboxSearchBase); } else { s = folder; } for (; delimiter && (e = strchr(s, delimiter)); *e = delimiter, s = e+1) { *e = '\0'; if (!strlen(s)) { continue; } while (*mPtrPtr && 0 > strcmp((*mPtrPtr)->name, s)) { mPtrPtr = &(*mPtrPtr)->next; } if (!*mPtrPtr || strcmp((*mPtrPtr)->name, s)) { nPtr = (Mailbox*)ckalloc(sizeof(Mailbox)+strlen(s)*3+1); nPtr->name = (char*)nPtr+sizeof(Mailbox); strcpy(nPtr->name, (do_decode ? RatMutf7toUtf8(s) : s)); nPtr->folder = NULL; nPtr->attributes = LATT_NOSELECT; nPtr->next = *mPtrPtr; nPtr->child = NULL; *mPtrPtr = nPtr; mPtrPtr = &nPtr->child; } else { mPtrPtr = &(*mPtrPtr)->child; } } if (attributes & LATT_NOSELECT) { return; } /* * Find location and link it */ while (*mPtrPtr && 0 > strcmp((*mPtrPtr)->name, name)) { mPtrPtr = &(*mPtrPtr)->next; } /* * Ignore duplicates */ encoded = RatEncodeQP(folder); if (*mPtrPtr && (*mPtrPtr)->folder && !strcmp((*mPtrPtr)->folder, Tcl_DStringValue(encoded)) && (*mPtrPtr)->attributes == attributes) { Tcl_DStringFree(encoded); ckfree(encoded); return; } /* * Create actual folder entry */ nPtr = (Mailbox*)ckalloc( sizeof(Mailbox) + strlen(name)*3 + Tcl_DStringLength(encoded) + 2); nPtr->name = (char*)nPtr+sizeof(Mailbox); strcpy(nPtr->name, (do_decode ? RatMutf7toUtf8(name) : name)); nPtr->folder = nPtr->name+strlen(nPtr->name)+1; strcpy(nPtr->folder, Tcl_DStringValue(encoded)); nPtr->attributes = attributes; nPtr->delimiter = delimiter; nPtr->next = *mPtrPtr; nPtr->child = NULL; *mPtrPtr = nPtr; Tcl_DStringFree(encoded); ckfree(encoded); } void mm_lsub (MAILSTREAM *stream, int delimiter, char *name, long attributes) { mm_list(stream, delimiter, name, attributes | LATT_NOINFERIORS); } void mm_status (MAILSTREAM *stream, char *mailbox, MAILSTATUS *status) { memcpy(&stdStatus, status, sizeof(MAILSTATUS)); } #ifdef MEM_DEBUG void ratStdFolderCleanup() { ckfree(logMessage); } #endif /* MEM_DEBUG */