#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: alpined.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $"; #endif /* ======================================================================== * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ /* ======================================================================== Implement alpine TCL interfaces. Execute TCL interfaces via interpreter reading commands and writing results over UNIX domain socket. ======================================================================== */ #include #include #include "../../../c-client/c-client.h" #include "../../../c-client/imap4r1.h" #include "../../../pith/osdep/color.h" /* color support library */ #include "../../../pith/osdep/canaccess.h" #include "../../../pith/osdep/temp_nam.h" #include "../../../pith/osdep/collate.h" #include "../../../pith/osdep/filesize.h" #include "../../../pith/osdep/writ_dir.h" #include "../../../pith/osdep/err_desc.h" #include "../../../pith/stream.h" #include "../../../pith/context.h" #include "../../../pith/state.h" #include "../../../pith/msgno.h" #include "../../../pith/debug.h" #include "../../../pith/init.h" #include "../../../pith/conf.h" #include "../../../pith/conftype.h" #include "../../../pith/detoken.h" #include "../../../pith/flag.h" #include "../../../pith/help.h" #include "../../../pith/remote.h" #include "../../../pith/status.h" #include "../../../pith/mailcmd.h" #include "../../../pith/savetype.h" #include "../../../pith/save.h" #include "../../../pith/reply.h" #include "../../../pith/sort.h" #include "../../../pith/ldap.h" #include "../../../pith/addrbook.h" #include "../../../pith/ablookup.h" #include "../../../pith/takeaddr.h" #include "../../../pith/bldaddr.h" #include "../../../pith/copyaddr.h" #include "../../../pith/thread.h" #include "../../../pith/folder.h" #include "../../../pith/mailview.h" #include "../../../pith/indxtype.h" #include "../../../pith/icache.h" #include "../../../pith/mailindx.h" #include "../../../pith/mailpart.h" #include "../../../pith/mimedesc.h" #include "../../../pith/detach.h" #include "../../../pith/newmail.h" #include "../../../pith/charset.h" #include "../../../pith/util.h" #include "../../../pith/rfc2231.h" #include "../../../pith/string.h" #include "../../../pith/send.h" #include "../../../pith/options.h" #include "../../../pith/list.h" #include "../../../pith/mimetype.h" #include "../../../pith/mailcap.h" #include "../../../pith/sequence.h" #include "../../../pith/smime.h" #include "../../../pith/url.h" #include "../../../pith/charconv/utf8.h" #include "alpined.h" #include "color.h" #include "imap.h" #include "ldap.h" #include "debug.h" #include "stubs.h" #include /* * Fake screen dimension for word wrap and such */ #define FAKE_SCREEN_WIDTH 80 #define FAKE_SCREEN_LENGTH 24 /* * Arbitrary minimum display width (in characters) */ #define MIN_SCREEN_COLS 20 /* * Maximum number of lines allowed in signatures */ #define SIG_MAX_LINES 24 #define SIG_MAX_COLS 1024 /* * Number of seconds we'll wait before we assume the client has wondered * on to more interesting content */ #define PE_INPUT_TIMEOUT 1800 /* * Posting error length max */ #define WP_MAX_POST_ERROR 128 /* * AUTH Response Tokens */ #define AUTH_EMPTY_STRING "NOPASSWD" #define AUTH_FAILURE_STRING "BADPASSWD" /* * CERT Response Tokens */ #define CERT_QUERY_STRING "CERTQUERY" #define CERT_FAILURE_STRING "CERTFAIL" /* * Charset used within alpined and to communicate with alpined * Note: posting-charset still respected */ #define WP_INTERNAL_CHARSET "UTF-8" /* * Globals referenced throughout pine... */ struct pine *ps_global; /* THE global variable! */ /* * More global state */ long gPeITop, gPeICount; long gPeInputTimeout = PE_INPUT_TIMEOUT; long gPEAbandonTimeout = 0; /* * Authorization issues */ int peNoPassword, peCredentialError; int peCertFailure, peCertQuery; char peCredentialRequestor[CRED_REQ_SIZE]; char *peSocketName; char **peTSig; CONTEXT_S *config_context_list; STRLIST_S *peCertHosts; bitmap_t changed_feature_list; #define F_CH_ON(feature) (bitnset((feature),changed_feature_list)) #define F_CH_OFF(feature) (!F_CH_ON(feature)) #define F_CH_TURN_ON(feature) (setbitn((feature),changed_feature_list)) #define F_CH_TURN_OFF(feature) (clrbitn((feature),changed_feature_list)) #define F_CH_SET(feature,value) ((value) ? F_CH_TURN_ON((feature)) \ : F_CH_TURN_OFF((feature))) typedef struct _status_msg { time_t posted; unsigned type:3; unsigned seen:1; long id; char *text; struct _status_msg *next; } STATMSG_S; static STATMSG_S *peStatList; typedef struct _composer_attachment { unsigned file:1; unsigned body:1; char *id; union { struct { char *local; char *remote; char *type; char *subtype; char *description; long size; } f; struct { BODY *body; } b; struct { long msgno; char *part; } msg; } l; struct _composer_attachment *next; } COMPATT_S; static COMPATT_S *peCompAttach; /* * Holds data passed */ typedef struct _msg_data { ENVELOPE *outgoing; METAENV *metaenv; PINEFIELD *custom; STORE_S *msgtext; STRLIST_S *attach; char *fcc; int fcc_colid; int postop_fcc_no_attach; char *charset; char *priority; int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *); unsigned flowed:1; unsigned html:1; unsigned qualified_addrs:1; } MSG_COL_S; /* * locally global structure to keep track of various bits of state * needed to collect filtered output */ static struct _embedded_data { Tcl_Interp *interp; Tcl_Obj *obj; STORE_S *store; long uid; HANDLE_S *handles; char inhandle; ENVELOPE *env; BODY *body; struct { char fg[7]; char bg[7]; char fgdef[7]; char bgdef[7]; } color; } peED; /* * RSS stream cache */ typedef struct _rss_cache_s { char *link; time_t stale; int referenced; RSS_FEED_S *feed; } RSS_CACHE_S; #define RSS_NEWS_CACHE_SIZE 1 #define RSS_WEATHER_CACHE_SIZE 1 #ifdef ENABLE_LDAP WPLDAP_S *wpldap_global; #endif /* * random string generator flags */ #define PRS_NONE 0x0000 #define PRS_LOWER_CASE 0x0001 #define PRS_UPPER_CASE 0x0002 #define PRS_MIXED_CASE 0x0004 /* * peSaveWork flag definitions */ #define PSW_NONE 0x00 #define PSW_COPY 0x01 #define PSW_MOVE 0x02 /* * Message Collector flags */ #define PMC_NONE 0x00 #define PMC_FORCE_QUAL 0x01 #define PMC_PRSRV_ATT 0x02 /* * length of thread info string */ #define WP_MAX_THRD_S 64 /* * static buf size for putenv() if necessary */ #define PUTENV_MAX 64 /*---------------------------------------------------------------------- General use big buffer. It is used in the following places: compose_mail: while parsing header of postponed message append_message2: while writing header into folder q_status_messageX: while doing printf formatting addr_book: Used to return expanded address in. (Can only use here because mm_log doesn't q_status on PARSE errors !) alpine.c: When address specified on command line init.c: When expanding variable values and many many more... ----*/ char tmp_20k_buf[20480]; /* Internal prototypes */ void peReturn(int, char *, const char *); int peWrite(int, char *); char *peCreateUserContext(Tcl_Interp *, char *, char *, char *); void peDestroyUserContext(struct pine **); char *peLoadConfig(struct pine *); int peCreateStream(Tcl_Interp *, CONTEXT_S *, char *, int); void peDestroyStream(struct pine *); void pePrepareForAuthException(void); char *peAuthException(void); void peInitVars(struct pine *); int peSelect(Tcl_Interp *, int, Tcl_Obj **, int); int peSelectNumber(Tcl_Interp *, int, Tcl_Obj **, int); int peSelectDate(Tcl_Interp *, int, Tcl_Obj **, int); int peSelectText(Tcl_Interp *, int, Tcl_Obj **, int); int peSelectStatus(Tcl_Interp *, int, Tcl_Obj **, int); char *peSelValTense(Tcl_Obj *); char *peSelValYear(Tcl_Obj *); char *peSelValMonth(Tcl_Obj *); char *peSelValDay(Tcl_Obj *); int peSelValCase(Tcl_Obj *); int peSelValField(Tcl_Obj *); int peSelValFlag(Tcl_Obj *); int peSelected(Tcl_Interp *, int, Tcl_Obj **, int); int peSelectError(Tcl_Interp *, char *); int peApply(Tcl_Interp *, int, Tcl_Obj **); char *peApplyFlag(MAILSTREAM *, MSGNO_S *, char, int, long *); int peApplyError(Tcl_Interp *, char *); int peIndexFormat(Tcl_Interp *); int peAppendIndexParts(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *); int peAppendIndexColor(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *); int peMessageStatusBits(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); char *peMsgStatBitString(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *); Tcl_Obj *peMsgStatNameList(Tcl_Interp *, struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *); int peNewMailResult(Tcl_Interp *); int peMessageSize(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageDate(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageSubject(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageFromAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageToAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageCcAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageField(Tcl_Interp *, imapuid_t, char *); int peMessageStatus(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageCharset(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageBounce(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageSpamNotice(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); char *peSendSpamReport(long, char *, char *, char *); int peMsgnoFromUID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageHeader(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); void peFormatEnvelope(MAILSTREAM *, long, char *, ENVELOPE *, gf_io_t, long, char *, int); void peFormatEnvelopeAddress(MAILSTREAM *, long, char *, char *, ADDRESS *, int, char *, gf_io_t); void peFormatEnvelopeNewsgroups(char *, char *, int, gf_io_t); void peFormatEnvelopeText(char *, char *); int peMessageAttachments(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessageBody(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMessagePartFromCID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peLocateBodyByCID(char *, char *, BODY *); char *peColorStr(char *, char *); int peInterpWritec(int); int peInterpFlush(void); int peNullWritec(int); void peGetMimeTyping(BODY *, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **); int peGetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peIsFlagged(MAILSTREAM *, imapuid_t, char *); int peSetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMsgSelect(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peReplyHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peAppListF(Tcl_Interp *, Tcl_Obj *, char *, ...); void pePatAppendID(Tcl_Interp *, Tcl_Obj *, PAT_S *); void pePatAppendPattern(Tcl_Interp *, Tcl_Obj *, PAT_S *); char *pePatStatStr(int); int peReplyText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peSoStrToList(Tcl_Interp *, Tcl_Obj *, STORE_S *); int peForwardHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peForwardText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peDetach(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peAttachInfo(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peSaveDefault(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peSaveWork(Tcl_Interp *, imapuid_t, int, Tcl_Obj **, long); int peSave(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peCopy(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peMove(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peGotoDefault(Tcl_Interp *, imapuid_t, Tcl_Obj **); int peTakeaddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peTakeFrom(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peAddSuggestedContactInfo(Tcl_Interp *, Tcl_Obj *, ADDRESS *); int peReplyQuote(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); long peMessageNumber(imapuid_t); long peSequenceNumber(imapuid_t); int peMsgCollector(Tcl_Interp *, int, Tcl_Obj **, int (*)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long); int peMsgCollected(Tcl_Interp *, MSG_COL_S *, char *, long); void peMsgSetParm(PARAMETER **, char *, char *); Tcl_Obj *peMsgAttachCollector(Tcl_Interp *, BODY *); int peFccAppend(Tcl_Interp *, Tcl_Obj *, char *, int); int peDoPost(METAENV *, BODY *, char *, CONTEXT_S **, char *); int peDoPostpone(METAENV *, BODY *, char *, CONTEXT_S **, char *); int peWriteSig (Tcl_Interp *, char *, Tcl_Obj **); int peInitAddrbooks(Tcl_Interp *, int); int peRuleStatVal(char *, int *); int peRuleSet(Tcl_Interp *, Tcl_Obj **); int peAppendCurrentSort(Tcl_Interp *interp); int peAppendDefaultSort(Tcl_Interp *interp); #if 0 ADDRESS *peAEToAddress(AdrBk_Entry *); char *peAEFcc(AdrBk_Entry *); #endif NAMEVAL_S *sort_key_rules(int); NAMEVAL_S *wp_indexheight_rules(int); PINEFIELD *peCustomHdrs(void); STATMSG_S *sml_newmsg(int, char *); char *sml_getmsg(void); char **sml_getmsgs(void); void sml_seen(void); #ifdef ENABLE_LDAP int peLdapQueryResults(Tcl_Interp *); int peLdapStrlist(Tcl_Interp *, Tcl_Obj *, struct berval **); int init_ldap_pname(struct pine *); #endif /* ENABLE_LDAP */ char *strqchr(char *, int, int *, int); Tcl_Obj *wp_prune_folders(CONTEXT_S *, char *, int, char *, unsigned, int *, int, Tcl_Interp *); int hex_colorstr(char *, char *); int hexval(char); int ascii_colorstr(char *, char *); COMPATT_S *peNewAttach(void); void peFreeAttach(COMPATT_S **); COMPATT_S *peGetAttachID(char *); char *peFileAttachID(char *, char *, char *, char *, char *, int); char *peBodyAttachID(BODY *); void peBodyMoveContents(BODY *, BODY *); int peClearAttachID(char *); char *peRandomString(char *, int, int); void ms_init(STRING *, void *, unsigned long); char ms_next(STRING *); void ms_setpos(STRING *, unsigned long); long peAppendMsg(MAILSTREAM *, void *, char **, char **, STRING **); int remote_pinerc_failure(void); char *peWebAlpinePrefix(void); void peNewMailAnnounce(MAILSTREAM *, long, long); int peMessageNeedPassphrase(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); int peRssReturnFeed(Tcl_Interp *, char *, char *); int peRssPackageFeed(Tcl_Interp *, RSS_FEED_S *); RSS_FEED_S *peRssFeed(Tcl_Interp *, char *, char *); RSS_FEED_S *peRssFetch(Tcl_Interp *, char *); void peRssComponentFree(char **,char **,char **,char **,char **,char **); void peRssClearCacheEntry(RSS_CACHE_S *); /* Prototypes for Tcl-exported methods */ int PEInit(Tcl_Interp *interp, char *); void PEExitCleanup(ClientData); int PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); int PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); /* Append package */ typedef struct append_pkg { MAILSTREAM *stream; /* source stream */ unsigned long msgno; /* current message number */ unsigned long msgmax; /* maximum message number */ char *flags; /* current flags */ char *date; /* message internal date */ STRING *message; /* stringstruct of message */ } APPEND_PKG; STRINGDRIVER mstring = { ms_init, /* initialize string structure */ ms_next, /* get next byte in string structure */ ms_setpos /* set position in string structure */ }; /*---------------------------------------------------------------------- main routine -- entry point Args: argv, argc -- The command line arguments Setup c-client drivers and dive into TCL interpreter engine ----*/ int main(int argc, char *argv[]) { int ev = 1, s, cs, n, co, o, l, bl = 256, argerr; char *buf, sname[256]; struct sockaddr_un name; Tcl_Interp *interp; #if PUBCOOKIE extern AUTHENTICATOR auth_gss_proxy; #endif srandom(getpid() + time(0)); /*---------------------------------------------------------------------- Initialize c-client ----------------------------------------------------------------------*/ /* * NO LOCAL DRIVERS ALLOWED * For this to change pintecld *MUST* be running under the user's UID and * and signal.[ch] need to get fixed to handle KOD rather than change * the debug level */ mail_link (&imapdriver); /* link in the imap driver */ mail_link (&unixdriver); /* link in the unix driver */ mail_link (&dummydriver); /* link in the dummy driver */ /* link authentication drivers */ #if PUBCOOKIE auth_link (&auth_gss_proxy); /* pubcoookie proxy authenticator */ #endif auth_link (&auth_md5); /* link in the md5 authenticator */ auth_link (&auth_pla); auth_link (&auth_log); /* link in the log authenticator */ ssl_onceonlyinit (); mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2); #if PUBCOOKIE /* if REMOTE_USER set, use it as username */ if(buf = getenv("REMOTE_USER")) env_init(buf, "/tmp"); #endif if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){ fprintf(stderr, "Can't disable unix driver"); exit(1); } /* * Set network timeouts so we don't hang forever * The open timeout can be pretty short since we're * just opening tcp connection. The read timeout needs * to be longer because the response to some actions can * take awhile. Hopefully this is well within httpd's * cgi timeout threshold. */ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30); mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60); /*---------------------------------------------------------------------- Initialize pith library ----------------------------------------------------------------------*/ pith_opt_remote_pinerc_failure = remote_pinerc_failure; pith_opt_user_agent_prefix = peWebAlpinePrefix; pith_opt_newmail_announce = peNewMailAnnounce; setup_for_index_index_screen(); /*---------------------------------------------------------------------- Parse arguments ----------------------------------------------------------------------*/ debug = 0; for(argerr = 0; !argerr && ((n = getopt(argc,argv,"d")) != -1); ) { switch(n) { case 'd' : debug++; break; case '?' : argerr = 1; break; } } if(argerr || optind != argc){ char *p = strrchr(argv[0],'/'); fprintf(stderr, "Usage: %s [-d]\n", p ? p + 1 : argv[0]); exit(1); } /*---------------------------------------------------------------------- Hop into the Tcl processing loop ----------------------------------------------------------------------*/ buf = (char *) fs_get(bl * sizeof(char)); if(fgets(sname, 255, stdin) && *sname){ if(sname[l = strlen(sname) - 1] == '\n') sname[l] = '\0'; if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){ name.sun_family = AF_UNIX; strcpy(name.sun_path, peSocketName = sname); l = sizeof(name); if(bind(s, (struct sockaddr *) &name, l) == 0){ if(listen(s, 5) == 0){ /* * after the groundwork's done, go into the background. * the fork saves the caller from invoking us in the background * which introduces a timing race between the first client * request arrival and our being prepared to accept it. */ if(debug < 10){ switch(fork()){ case -1 : /* error */ perror("fork"); exit(1); case 0 : /* child */ close(0); /* disassociate */ close(1); close(2); setpgrp(0, 0); break; default : /* parent */ exit(0); } } debug_init(); dprint((SYSDBG_INFO, "started")); interp = Tcl_CreateInterp(); PEInit(interp, sname); while(1){ struct timeval tv; fd_set rfd; FD_ZERO(&rfd); FD_SET(s, &rfd); tv.tv_sec = (gPEAbandonTimeout) ? gPEAbandonTimeout : gPeInputTimeout; tv.tv_usec = 0; if((n = select(s+1, &rfd, 0, 0, &tv)) > 0){ socklen_t ll = l; gPEAbandonTimeout = 0; if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){ dprint((SYSDBG_ERR, "accept failure: %s", error_description(errno))); break; } dprint((5, "accept success: %d", cs)); /* * tcl commands are prefixed with a number representing * the length of the command string and a newline character. * the characters representing the length and the newline * are not included in the command line length calculation. */ o = co = 0; while((n = read(cs, buf + o, bl - o - 1)) > 0){ o += n; if(!co){ int i, x = 0; for(i = 0; i < o; i++) if(buf[i] == '\n'){ co = ++i; l = x + co; if(bl < l + 1){ bl = l + 1; fs_resize((void **) &buf, bl * sizeof(char)); } break; } else x = (x * 10) + (buf[i] - '0'); } if(o && o == l) break; } if(n == 0){ dprint((SYSDBG_ERR, "read EOF")); } else if(n < 0){ dprint((SYSDBG_ERR, "read failure: %s", error_description(errno))); } else{ buf[o] = '\0'; /* Log every Eval if somebody *really* wants to see it. */ if(debug > 6){ char dbuf[5120]; int dlim = (debug >= 9) ? 256 : 5120 - 32; snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]); /* But DON'T log any clear-text credentials */ if(dbuf[9] == 'P' && dbuf[10] == 'E' && dbuf[11] == 'S' && !strncmp(dbuf + 12, "ession creds ", 13)){ char *p; for(p = &dbuf[25]; *p; p++) *p = 'X'; } dprint((1, dbuf)); } switch(Tcl_Eval(interp, &buf[co])){ case TCL_OK : peReturn(cs, "OK", Tcl_GetStringResult(interp)); break; case TCL_ERROR : peReturn(cs, "ERROR", Tcl_GetStringResult(interp)); break; case TCL_BREAK : peReturn(cs, "BREAK", Tcl_GetStringResult(interp)); break; case TCL_RETURN : peReturn(cs, "RETURN", Tcl_GetStringResult(interp)); break; default : peReturn(cs, "BOGUS", "eval returned unexpected value"); break; } } close(cs); } else if(errno != EINTR){ if(n < 0){ dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno))); } else{ dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec)); } Tcl_Exit(0); /* Tcl_Exit should never return. Getting here is an error. */ dprint((SYSDBG_ERR, "Tcl_Exit failure")); } } } else perror("listen"); } else perror("bind"); close(s); unlink(sname); } else perror("socket"); } else fprintf(stderr, "Can't read socket name\n"); exit(ev); } /* * peReturn - common routine to return TCL result */ void peReturn(int sock, char *status, const char *result) { if(peWrite(sock, status)) if(peWrite(sock, "\n")) peWrite(sock, (char *) result); } /* * peWrite - write all the given string on the given socket */ int peWrite(int sock, char *s) { int i, n; for(i = 0, n = strlen(s); n; n = n - i) if((i = write(sock, s + i, n)) < 0){ dprint((SYSDBG_ERR, "write: %s", error_description(errno))); return(0); } return(1); } /* * PEInit - Initialize exported TCL functions */ int PEInit(Tcl_Interp *interp, char *sname) { dprint((2, "PEInit: %s", sname)); if(Tcl_Init(interp) == TCL_ERROR) { return(TCL_ERROR); } Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PESession", PESessionCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "PERss", PERssCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateExitHandler(PEExitCleanup, sname); #ifdef ENABLE_LDAP wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S)); wpldap_global->query_no = 0; wpldap_global->ldap_search_list = NULL; #endif /* ENABLE_LDAP */ return(TCL_OK); } void PEExitCleanup(ClientData clientData) { dprint((4, "PEExitCleanup")); if(ps_global){ /* destroy any open stream */ peDestroyStream(ps_global); /* destroy user context */ peDestroyUserContext(&ps_global); } #ifdef ENABLE_LDAP if(wpldap_global){ if(wpldap_global->ldap_search_list) free_wpldapres(wpldap_global->ldap_search_list); fs_give((void **)&wpldap_global); } #endif /* ENABLE_LDAP */ if((char *) clientData) unlink((char *) clientData); peFreeAttach(&peCompAttach); dprint((SYSDBG_INFO, "finished")); } /* * PEInfoCmd - export various bits of alpine state */ int PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err = "Unknown PEInfo request"; dprint((2, "PEInfoCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else{ char *s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(!strcmp(s1, "colorset")){ char *varname, *fghex, *bghex; char tvname[256], asciicolor[256]; struct variable *vtmp; Tcl_Obj **cObj; int cObjc; SPEC_COLOR_S *hcolors, *thc; if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC); return(TCL_ERROR); } if(!strcmp(varname, "viewer-hdr-colors")){ char *newhdr = NULL, *newpat = NULL, *utype; int hindex, i; if(objc < 5){ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC); return(TCL_ERROR); } hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0); if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC); return(TCL_ERROR); } if(!strcmp(utype, "delete")){ if(!hcolors){ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC); return(TCL_ERROR); } if(hindex == 0){ thc = hcolors; hcolors = hcolors->next; thc->next = NULL; free_spec_colors(&thc); } else{ /* zero based */ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++) ; if(thc && thc->next){ SPEC_COLOR_S *thc2 = thc->next; thc->next = thc2->next; thc2->next = NULL; free_spec_colors(&thc2); } else{ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC); return(TCL_ERROR); } } } else if(!strcmp(utype, "add")){ if(objc != 6){ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC); return(TCL_ERROR); } if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK) return (TCL_ERROR); if(cObjc != 2){ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC); return(TCL_ERROR); } newhdr = Tcl_GetStringFromObj(cObj[0], NULL); newpat = Tcl_GetStringFromObj(cObj[1], NULL); if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK) return (TCL_ERROR); if(cObjc != 2){ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC); return(TCL_ERROR); } fghex = Tcl_GetStringFromObj(cObj[0], NULL); bghex = Tcl_GetStringFromObj(cObj[1], NULL); if(newhdr && newpat && fghex && bghex){ SPEC_COLOR_S **hcp; for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next) ; *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S)); (*hcp)->inherit = 0; (*hcp)->spec = cpystr(newhdr); (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black"); (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white"); if(newpat && *newpat) (*hcp)->val = string_to_pattern(newpat); else (*hcp)->val = NULL; (*hcp)->next = NULL; } else{ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC); return(TCL_ERROR); } } else if(!strcmp(utype, "update")){ if(objc != 6){ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC); return(TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK && cObjc == 3 && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL)) && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC); return (TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK && cObjc == 2 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL)) && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC); return (TCL_ERROR); } for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++) ; if(!thc){ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC); return (TCL_ERROR); } if(thc->spec) fs_give((void **)&thc->spec); thc->spec = cpystr(newhdr); if(ascii_colorstr(asciicolor, fghex) == 0) { if(thc->fg) fs_give((void **)&thc->fg); thc->fg = cpystr(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, bghex) == 0) { if(thc->bg) fs_give((void **)&thc->bg); thc->bg = cpystr(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(thc->val) fs_give((void **)&thc->val); if(newpat && *newpat){ thc->val = string_to_pattern(newpat); } } else{ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC); return(TCL_ERROR); } vtmp = &ps_global->vars[V_VIEW_HDR_COLORS]; for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++) fs_give((void **)&vtmp->main_user_val.l[i]); if(vtmp->main_user_val.l) fs_give((void **)&vtmp->main_user_val.l); vtmp->main_user_val.l = varlist_from_spec_colors(hcolors); set_current_val(vtmp, FALSE, FALSE); free_spec_colors(&hcolors); return(TCL_OK); } else { if(objc != 4){ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC); return(TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK && cObjc == 2 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL)) && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC); return (TCL_ERROR); } snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname); for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name || vtmp->is_list){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, fghex) == 0) { if(vtmp->main_user_val.p) fs_give((void **)&vtmp->main_user_val.p); vtmp->main_user_val.p = cpystr(asciicolor); set_current_val(vtmp, FALSE, FALSE); if(!strucmp(varname, "normal")) pico_set_fg_color(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color"); vtmp++; if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name) for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name || vtmp->is_list){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, bghex) == 0) { if(vtmp->main_user_val.p) fs_give((void **)&vtmp->main_user_val.p); vtmp->main_user_val.p = cpystr(asciicolor); set_current_val(vtmp, FALSE, FALSE); if(!strucmp(varname, "normal")) pico_set_bg_color(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } Tcl_SetResult(interp, "1", TCL_STATIC); return(TCL_OK); } } else if(!strcmp(s1, "lappend")){ if(objc >= 4){ Tcl_Obj *dObj; int i; if((dObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){ for(i = 3; i < objc; i++) if(Tcl_ListObjAppendElement(interp, dObj, objv[i]) != TCL_OK) return(TCL_ERROR); if(i == objc){ return(TCL_OK); } } else err = "PEInfo lappend: Unknown list name"; } else err = "PEInfo lappend: Too few args"; } else if(objc == 2){ if(!strcmp(s1, "version")){ char buf[256]; /* * CMD: version * * Returns: string representing Pine version * engine built on */ Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "revision")){ char buf[16]; /* * CMD: revision * * Returns: string representing Pine SVN revision * engine built on */ Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "key")){ static char key[64]; if(!key[0]) peRandomString(key,32,PRS_UPPER_CASE); Tcl_SetResult(interp, key, TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "indexheight")){ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXHEIGHT ? ps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "indexlines")){ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXLINES ? ps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "aggtabstate")){ Tcl_SetResult(interp, ps_global->VAR_WP_AGGSTATE ? ps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "alpinestate")){ char *wps, *p, *q; if((wps = ps_global->VAR_WP_STATE) != NULL){ wps = p = q = cpystr(wps); do if(*q == '\\' && *(q+1) == '$') q++; while((*p++ = *q++) != '\0'); } Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE); if(wps) fs_give((void **) &wps); return(TCL_OK); } else if(!strcmp(s1, "foreground")){ char *color; if(!((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color)) && (color = peColorStr(color,tmp_20k_buf)))) color = "000000"; Tcl_SetResult(interp, color, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "background")){ char *color; if(!((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color)) && (color = peColorStr(color,tmp_20k_buf)))) color = "FFFFFF"; Tcl_SetResult(interp, color, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "flaglist")){ int i; char *p; Tcl_Obj *itemObj; /* * BUG: This list should get merged with the static list in "cmd_flag" * and exported via some function similar to "feature_list()" */ static char *flag_list[] = { "Important", "New", "Answered", "Deleted", NULL }; /* * CMD: flaglist * * Returns: list of FLAGS available for setting */ for(i = 0; (p = flag_list[i]); i++) if((itemObj = Tcl_NewStringObj(p, -1)) != NULL){ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj) != TCL_OK) ; } return(TCL_OK); } else if(!strcmp(s1, "featurelist")){ int i; char *curfeature, *s; FEATURE_S *feature; Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL; /* * CMD: featurelist * * Returns: list of FEATURES available for setting */ for(i = 0, curfeature = NULL; (feature = feature_list(i)); i++) if((s = feature_list_section(feature)) != NULL){ if(!curfeature || strucmp(s, curfeature)){ if(resObj) { Tcl_ListObjAppendElement(interp, secObj, resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj); } secObj = Tcl_NewListObj(0, NULL); resObj = Tcl_NewListObj(0, NULL); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(s,-1)) != TCL_OK) ; curfeature = s; } if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){ if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK) ; } } if(resObj){ Tcl_ListObjAppendElement(interp, secObj, resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj); } return(TCL_OK); } else if(!strcmp(s1, "featuresettings")){ int i; FEATURE_S *feature; Tcl_Obj *itemObj; /* * CMD: featuresettings * * Returns: list of FEATURES currently SET */ for(i = 0; (feature = feature_list(i)); i++) if(feature_list_section(feature)){ if(F_ON(feature->id, ps_global)){ if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj) != TCL_OK) ; } } } return(TCL_OK); } else if(!strcmp(s1, "signature")){ char *sig; if((ps_global->VAR_LITERAL_SIG || (ps_global->VAR_SIGNATURE_FILE && IS_REMOTE(ps_global->VAR_SIGNATURE_FILE))) && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){ char *p, *q; for(p = sig; (q = strindex(p, '\n')); p = q + 1) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, q - p)); if(*p) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, -1)); fs_give((void **) &sig); } else Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "rawsig")){ char *err = NULL, *sig = NULL, *p, *q; if(ps_global->VAR_LITERAL_SIG){ char *err = NULL; char **apval; if(ps_global->restricted){ err = "Alpine demo can't change config file"; } else{ /* BUG: no "exceptions file" support */ if((apval = APVAL(&ps_global->vars[V_LITERAL_SIG], Main)) != NULL){ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char)); sig[0] = '\0'; cstring_to_string(*apval, sig); } else err = "Problem accessing configuration"; } } else if(!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)) snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s", ps_global->VAR_SIGNATURE_FILE ? ps_global->VAR_SIGNATURE_FILE : ""); else if(!(sig = simple_read_remote_file(ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))) err = "Can't read remote pinerc"; if(err){ Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } for(p = sig; (q = strindex(p, '\n')); p = q + 1) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, q - p)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, -1)); fs_give((void **) &sig); return(TCL_OK); } else if(!strcmp(s1, "statmsg")){ char *s = sml_getmsg(); /* BUG: can this be removed? */ Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "statmsgs")){ char **s = sml_getmsgs(); char **tmps, *lmsg = NULL; for(tmps = s; tmps && *tmps; lmsg = *tmps++) if(!lmsg || strcmp(lmsg, *tmps)) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(*tmps, -1)); fs_give((void **)&s); return(TCL_OK); } else if(!strcmp(s1, "saveconf")){ write_pinerc(ps_global, Main, WRP_NOUSER); return(TCL_OK); } else if(!strucmp(s1, "sort")){ return(peAppendDefaultSort(interp)); } else if(!strcmp(s1, "ldapenabled")){ /* * CMD: ldapenabled * * Returns: 1 if enabled 0 if not */ #ifdef ENABLE_LDAP Tcl_SetResult(interp, "1", TCL_VOLATILE); #else Tcl_SetResult(interp, "0", TCL_VOLATILE); #endif return(TCL_OK); } else if(!strcmp(s1, "prunecheck")){ time_t now; struct tm *tm_now; char tmp[50]; if(!check_prune_time(&now, &tm_now)){ Tcl_SetResult(interp, "0", TCL_VOLATILE); return(TCL_OK); } else { /* * We're going to reset the last-time-pruned variable * so that it asks a maximum of 1 time per month. * PROs: Annoying-factor is at its lowest * Can go ahead and move folders right away if * pruning-rule is automatically set to do so * CONs: Annoying-factor is at its lowest, if it's set * later then we can ensure that the questions * actually get answered or it will keep asking */ ps_global->last_expire_year = tm_now->tm_year; ps_global->last_expire_month = tm_now->tm_mon; snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year, ps_global->last_expire_month + 1); set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main); Tcl_SetResult(interp, "1", TCL_VOLATILE); } return(TCL_OK); } else if(!strcmp(s1, "prunetime")){ time_t now; struct tm *tm_now; CONTEXT_S *prune_cntxt; Tcl_Obj *retObj = NULL; int cur_month, ok = 1; char **p; static int moved_fldrs = 0; now = time((time_t *)0); tm_now = localtime(&now); cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon; if(!(prune_cntxt = default_save_context(ps_global->context_list))) prune_cntxt = ps_global->context_list; if(prune_cntxt){ if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC && context_isambig(ps_global->VAR_DEFAULT_FCC)) if((retObj = wp_prune_folders(prune_cntxt, ps_global->VAR_DEFAULT_FCC, cur_month, "sent", ps_global->pruning_rule, &ok, moved_fldrs, interp)) != NULL) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), retObj); if(ok && ps_global->VAR_READ_MESSAGE_FOLDER && *ps_global->VAR_READ_MESSAGE_FOLDER && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER)) if((retObj = wp_prune_folders(prune_cntxt, ps_global->VAR_READ_MESSAGE_FOLDER, cur_month, "read", ps_global->pruning_rule, &ok, moved_fldrs, interp)) != NULL) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), retObj); if(ok && (p = ps_global->VAR_PRUNED_FOLDERS)){ for(; ok && *p; p++) if(**p && context_isambig(*p)) if((retObj = wp_prune_folders(prune_cntxt, *p, cur_month, "", ps_global->pruning_rule, &ok, moved_fldrs, interp)) != NULL) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), retObj); } } moved_fldrs = 1; return(TCL_OK); } else if(!strcmp(s1, "authrequestor")){ Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "noop")){ /* tickle the imap server too */ if(ps_global->mail_stream) pine_mail_ping(ps_global->mail_stream); Tcl_SetResult(interp, "NOOP", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "inputtimeout")){ Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE); return(TCL_OK); } } else if(objc == 3){ if(!strcmp(s1, "feature")){ char *featurename; int i, isset = 0; FEATURE_S *feature; /* * CMD: feature * * ARGS: featurename - * * Returns: 1 if named feature set, 0 otherwise * */ if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) != NULL) for(i = 0; (feature = feature_list(i)); i++) if(!strucmp(featurename, feature->name)){ isset = F_ON(feature->id, ps_global); break; } Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "colorget")){ char *varname; char tvname[256], hexcolor[256]; struct variable *vtmp; if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){ return(TCL_ERROR); } if(strcmp("viewer-hdr-colors", varname) == 0){ SPEC_COLOR_S *hcolors, *thc; Tcl_Obj *resObj; char hexcolor[256], *tstr = NULL; hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0); for(thc = hcolors; thc; thc = thc->next){ resObj = Tcl_NewListObj(0,NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(thc->spec, -1)); hex_colorstr(hexcolor, thc->fg); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(hexcolor, -1)); hex_colorstr(hexcolor, thc->bg); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(hexcolor, -1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(thc->val ? tstr = pattern_to_string(thc->val) : "", -1)); if(tstr) fs_give((void **)&tstr); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); } fs_give((void **)&hcolors); return(TCL_OK); } else { snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color"); for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++); if(!vtmp->name) return(TCL_ERROR); if(vtmp->is_list) return(TCL_ERROR); if(!vtmp->current_val.p) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("", -1)); else{ hex_colorstr(hexcolor, vtmp->current_val.p); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(hexcolor, -1)); } snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color"); vtmp++; if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name) for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name) return(TCL_ERROR); if(vtmp->is_list) return(TCL_ERROR); if(!vtmp->current_val.p) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("", -1)); else{ hex_colorstr(hexcolor, vtmp->current_val.p); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(hexcolor, -1)); } } return(TCL_OK); } else if(!strcmp(s1, "varget")){ struct variable *vtmp; Tcl_Obj *itemObj, *resObj, *secObj; char *vallist, *varname, tmperrmsg[256]; int i; NAMEVAL_S *tmpnv; /* * CMD: varget * * Returns: get the values for the requested variable * * The list returned follows this general form: * * char *; variable name * char **; list of set values * char *; display type (listbox, text, textarea, ...) * char **; list of possible values * (so far this is only useful for listboxes) */ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){ Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj", TCL_VOLATILE); return(TCL_ERROR); } for(vtmp = ps_global->vars; vtmp->name && strucmp(vtmp->name, varname); vtmp++) ; if(!vtmp->name){ snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s", strlen(varname) < 200 ? varname : ""); Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE); return(TCL_ERROR); } if((itemObj = Tcl_NewStringObj(vtmp->name, -1)) != NULL){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj); resObj = Tcl_NewListObj(0, NULL); if(vtmp->is_list){ for(i = 0 ; vtmp->current_val.l && vtmp->current_val.l[i]; i++){ vallist = vtmp->current_val.l[i]; if(*(vallist)) itemObj = Tcl_NewStringObj(vallist, -1); else itemObj = Tcl_NewStringObj("", -1); Tcl_ListObjAppendElement(interp, resObj, itemObj); } } else{ itemObj = Tcl_NewStringObj(vtmp->current_val.p ? vtmp->current_val.p : "", -1); Tcl_ListObjAppendElement(interp, resObj, itemObj); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); secObj = Tcl_NewListObj(0, NULL); if(vtmp->is_list) itemObj = Tcl_NewStringObj("textarea", -1); else{ NAMEVAL_S *(*tmpf)(int); switch(vtmp - ps_global->vars){ case V_SAVED_MSG_NAME_RULE: tmpf = save_msg_rules; break; case V_FCC_RULE: tmpf = fcc_rules; break; case V_SORT_KEY: tmpf = sort_key_rules; break; case V_AB_SORT_RULE: tmpf = ab_sort_rules; break; case V_FLD_SORT_RULE: tmpf = fld_sort_rules; break; case V_GOTO_DEFAULT_RULE: tmpf = goto_rules; break; case V_INCOMING_STARTUP: tmpf = incoming_startup_rules; break; case V_PRUNING_RULE: tmpf = pruning_rules; break; case V_WP_INDEXHEIGHT: tmpf = wp_indexheight_rules; break; default: tmpf = NULL; break; } if(tmpf){ for(i = 0; (tmpnv = (tmpf)(i)); i++){ itemObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, itemObj, Tcl_NewStringObj(tmpnv->name, -1)); if(tmpnv->shortname) Tcl_ListObjAppendElement(interp, itemObj, Tcl_NewStringObj(tmpnv->shortname, -1)); Tcl_ListObjAppendElement(interp, secObj, itemObj); } itemObj = Tcl_NewStringObj("listbox", -1); } else itemObj = Tcl_NewStringObj("text", -1); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj); } return(TCL_OK); } else if(!strcmp(s1, "rawsig")){ if(ps_global->VAR_LITERAL_SIG){ char *cstring_version, *sig, *line; int i, nSig; Tcl_Obj **objSig; tmp_20k_buf[0] = '\0'; Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig); for(i = 0; i < nSig && i < SIG_MAX_LINES; i++) if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL) snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line); sig = cpystr(tmp_20k_buf); if((cstring_version = string_to_cstring(sig)) != NULL){ set_variable(V_LITERAL_SIG, cstring_version, 0, 0, Main); fs_give((void **)&cstring_version); } fs_give((void **) &sig); return(TCL_OK); } else return(peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE, &((Tcl_Obj **)objv)[2])); } else if(!strcmp(s1, "statmsg")){ char *msg; /* * CMD: statmsg * * ARGS: msg - text to set * * Returns: nothing, but with global status message * buf set to given msg * */ if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL) sml_addmsg(0, msg); return(TCL_OK); } else if(!strcmp(s1, "mode")){ char *mode; int rv = 0; /* * CMD: mode * * ARGS: * * Returns: return value of given binary mode * */ if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(!strcmp(mode, "full-header-mode")) rv = ps_global->full_header; } Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "indexlines")){ int n; char *p; if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){ set_variable(V_WP_INDEXLINES, p = int2string(n), 0, 0, Main); Tcl_SetResult(interp, p, TCL_VOLATILE); } return(TCL_OK); } else if(!strcmp(s1, "aggtabstate")){ int n; char *p; if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){ set_variable(V_WP_AGGSTATE, p = int2string(n), 0, 0, Main); Tcl_SetResult(interp, p, TCL_VOLATILE); } return(TCL_OK); } else if(!strcmp(s1, "alpinestate")){ char *wps, *p, *q, *twps = NULL; int dollars = 0; if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ for(p = wps; *p; p++) if(*p == '$') dollars++; if(dollars){ twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char)); p = wps; q = twps; do{ if(*p == '$') *q++ = '\\'; } while((*q++ = *p++) != '\0'); } set_variable(V_WP_STATE, twps ? twps : wps, 0, 1, Main); Tcl_SetResult(interp, wps, TCL_VOLATILE); if(twps) fs_give((void **) &twps); } return(TCL_OK); } else if(!strcmp(s1, "set")){ Tcl_Obj *rObj; if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){ Tcl_SetObjResult(interp, rObj); return(TCL_OK); } else return(TCL_ERROR); } else if(!strcmp(s1, "unset")){ char *varname; return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR); } } else if(objc == 4){ if(!strcmp(s1, "feature")){ char *featurename; int i, set, wasset = 0; FEATURE_S *feature; /* * CMD: feature * * ARGS: featurename - * value - new value to assign flag * * Returns: 1 if named feature set, 0 otherwise * */ if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR) for(i = 0; (feature = feature_list(i)); i++) if(!strucmp(featurename, feature->name)){ if(set != F_ON(feature->id, ps_global)){ toggle_feature(ps_global, &ps_global->vars[V_FEATURE_LIST], feature, TRUE, Main); if(ps_global->prc) ps_global->prc->outstanding_pinerc_changes = 1; } break; } Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(s1, "help")){ HelpType text; int i; char **help_text, **ptext, *helpname, tmperrmsg[256], *function; Tcl_Obj *itemObj; struct variable *vtmp; FEATURE_S *ftmp; if(!(helpname = Tcl_GetStringFromObj(objv[2], NULL))){ Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj for helpname", TCL_VOLATILE); return(TCL_ERROR); } if(!(function = Tcl_GetStringFromObj(objv[3], NULL))){ Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj for function", TCL_VOLATILE); return(TCL_ERROR); } if(strucmp(function, "plain") == 0){ if((text = help_name2section(helpname, strlen(helpname))) == NO_HELP) return(TCL_OK); } else if(strucmp(function, "variable") == 0){ for(vtmp = ps_global->vars; vtmp->name && strucmp(vtmp->name, helpname); vtmp++); if(!vtmp->name) { snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s", strlen(helpname) < 200 ? helpname : ""); Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE); return(TCL_ERROR); } text = config_help(vtmp - ps_global->vars, 0); if(text == NO_HELP) return(TCL_OK); } else if(strucmp(function, "feature") == 0){ for(i = 0; (ftmp = feature_list(i)); i++){ if(!strucmp(helpname, ftmp->name)){ text = ftmp->help; break; } } if(!ftmp || text == NO_HELP){ return(TCL_OK); } } else { snprintf(tmperrmsg, sizeof(tmperrmsg), "Invalid function: %s", strlen(helpname) < 200 ? function : ""); Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE); return(TCL_ERROR); } /* assumption here is that HelpType is char ** */ help_text = text; for(ptext = help_text; *ptext; ptext++){ itemObj = Tcl_NewStringObj(*ptext, -1); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj); } return(TCL_OK); } else if(!strcmp(s1, "varset")){ char *varname, **tmpstrlist, *line; struct variable *vtmp; Tcl_Obj **objVal; int i, numlistvals = 0, strlistpos; if((varname = Tcl_GetStringFromObj(objv[2], NULL)) && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals, &objVal) == TCL_OK)){ for(vtmp = ps_global->vars; vtmp->name && strucmp(vtmp->name, varname); vtmp++); if(!vtmp->name){ return(TCL_ERROR); } else{ /* found the variable */ if(vtmp->is_list){ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++) fs_give((void **)&vtmp->main_user_val.l[i]); if(vtmp->main_user_val.l) fs_give((void **)&vtmp->main_user_val.l); if(numlistvals > 0){ tmpstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *)); for(i = 0, strlistpos = 0; i < numlistvals; i++){ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){ removing_leading_and_trailing_white_space(line); if(*line) tmpstrlist[strlistpos++] = cpystr(line); } } tmpstrlist[strlistpos] = NULL; vtmp->main_user_val.l = (char **)fs_get((strlistpos+1) * sizeof(char *)); for(i = 0; i <= strlistpos; i++) vtmp->main_user_val.l[i] = tmpstrlist[i]; fs_give((void **)&tmpstrlist); } set_current_val(vtmp, FALSE, FALSE); return(TCL_OK); } else{ if((line = Tcl_GetStringFromObj(objVal[0], NULL)) != NULL){ if(strucmp(vtmp->name, "reply-indent-string")) removing_leading_and_trailing_white_space(line); if(vtmp->main_user_val.p) fs_give((void **)&vtmp->main_user_val.p); if(*line) vtmp->main_user_val.p = cpystr(line); set_current_val(vtmp, FALSE, FALSE); return(TCL_OK); } } } } return(TCL_ERROR); } else if(!strcmp(s1, "mode")){ char *mode; int value, rv = 0; /* * CMD: mode * * ARGS: * * Returns: old value of binary mode we were told to set * */ if((mode = Tcl_GetStringFromObj(objv[2], NULL)) && Tcl_GetIntFromObj(interp, objv[3], &value) != TCL_ERROR){ if(!strcmp(mode, "full-header-mode")){ rv = ps_global->full_header; ps_global->full_header = value; } } Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "set")){ Tcl_Obj *rObj; if((rObj = Tcl_ObjSetVar2(interp, objv[2], NULL, objv[3], TCL_LEAVE_ERR_MSG)) != NULL){ Tcl_SetObjResult(interp, rObj); return(TCL_OK); } else return(TCL_ERROR); } } else err = "PEInfo: Too many arguments"; } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * PEConfigCmd - edit various alpine config variables * * The goal here is to remember what's changed, but not write to pinerc * until the user's actually chosen to save. */ int PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err = "Unknown PEConfig request"; char *s1; dprint((2, "PEConfigCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(!strcmp(s1, "colorset")){ char *varname, *fghex, *bghex; char tvname[256], asciicolor[256]; struct variable *vtmp; Tcl_Obj **cObj; int cObjc; SPEC_COLOR_S *hcolors, *thc; if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC); return(TCL_ERROR); } if(!strcmp(varname, "viewer-hdr-colors")){ char *newhdr = NULL, *newpat = NULL, *utype; int hindex, i; if(objc < 5){ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC); return(TCL_ERROR); } if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val) hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0); else hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0); if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC); return(TCL_ERROR); } if(!strcmp(utype, "delete")){ if(!hcolors){ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC); return(TCL_ERROR); } if(hindex == 0){ thc = hcolors; hcolors = hcolors->next; thc->next = NULL; free_spec_colors(&thc); } else{ /* zero based */ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++) ; if(thc && thc->next){ SPEC_COLOR_S *thc2 = thc->next; thc->next = thc2->next; thc2->next = NULL; free_spec_colors(&thc2); } else{ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC); return(TCL_ERROR); } } } else if(!strcmp(utype, "add")){ if(objc != 6){ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC); return(TCL_ERROR); } if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK) return (TCL_ERROR); if(cObjc != 2){ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC); return(TCL_ERROR); } newhdr = Tcl_GetStringFromObj(cObj[0], NULL); newpat = Tcl_GetStringFromObj(cObj[1], NULL); if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK) return (TCL_ERROR); if(cObjc != 2){ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC); return(TCL_ERROR); } fghex = Tcl_GetStringFromObj(cObj[0], NULL); bghex = Tcl_GetStringFromObj(cObj[1], NULL); if(newhdr && newpat && fghex && bghex){ SPEC_COLOR_S **hcp; for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next) ; *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S)); (*hcp)->inherit = 0; (*hcp)->spec = cpystr(newhdr); (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black"); (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white"); if(newpat && *newpat) (*hcp)->val = string_to_pattern(newpat); else (*hcp)->val = NULL; (*hcp)->next = NULL; } else{ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC); return(TCL_ERROR); } } else if(!strcmp(utype, "update")){ if(objc != 6){ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC); return(TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK && cObjc == 3 && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL)) && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC); return (TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK && cObjc == 2 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL)) && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC); return (TCL_ERROR); } for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++) ; if(!thc){ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC); return (TCL_ERROR); } if(thc->spec) fs_give((void **)&thc->spec); thc->spec = cpystr(newhdr); if(ascii_colorstr(asciicolor, fghex) == 0) { if(thc->fg) fs_give((void **)&thc->fg); thc->fg = cpystr(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, bghex) == 0) { if(thc->bg) fs_give((void **)&thc->bg); thc->bg = cpystr(asciicolor); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(thc->val) fs_give((void **)&thc->val); if(newpat && *newpat){ thc->val = string_to_pattern(newpat); } } else{ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC); return(TCL_ERROR); } vtmp = &ps_global->vars[V_VIEW_HDR_COLORS]; for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++) fs_give((void **)&vtmp->changed_val.l[i]); if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l); vtmp->changed_val.l = varlist_from_spec_colors(hcolors); vtmp->is_changed_val = 1; free_spec_colors(&hcolors); return(TCL_OK); } else { if(objc != 4){ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC); return(TCL_ERROR); } if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK && cObjc == 2 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL)) && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC); return (TCL_ERROR); } snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname); for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name || vtmp->is_list){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, fghex) == 0) { if(vtmp->changed_val.p) fs_give((void **)&vtmp->changed_val.p); vtmp->changed_val.p = cpystr(asciicolor); vtmp->is_changed_val = 1; /* We need to handle this in the actual config setting * if(!strucmp(varname, "normal")) * pico_set_fg_color(asciicolor); */ } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color"); vtmp++; if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name) for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name || vtmp->is_list){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } if(ascii_colorstr(asciicolor, bghex) == 0) { if(vtmp->changed_val.p) fs_give((void **)&vtmp->changed_val.p); vtmp->changed_val.p = cpystr(asciicolor); vtmp->is_changed_val = 1; /* again, we need to handle this when we actually set the variable * if(!strucmp(varname, "normal")) * pico_set_bg_color(asciicolor); */ } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } Tcl_SetResult(interp, "1", TCL_STATIC); return(TCL_OK); } } else if(!strcmp(s1, "ruleset")){ return(peRuleSet(interp, &((Tcl_Obj **)objv)[2])); } else if(objc == 2){ if(!strcmp(s1, "featuresettings")){ struct variable *vtmp; int i; FEATURE_S *feature; vtmp = &ps_global->vars[V_FEATURE_LIST]; for(i = 0; (feature = feature_list(i)); i++) if(feature_list_section(feature)){ if(vtmp->is_changed_val ? F_CH_ON(feature->id) : F_ON(feature->id, ps_global)){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(feature->name, -1)); } } return(TCL_OK); } else if(!strcmp(s1, "rawsig")){ char *err = NULL, *sig = NULL, *p, *q; int i; struct variable *vtmp; vtmp = &ps_global->vars[V_LITERAL_SIG]; if(vtmp->is_changed_val ? vtmp->changed_val.p : ps_global->VAR_LITERAL_SIG){ char *err = NULL; char **apval; if(ps_global->restricted){ err = "Alpine demo can't change config file"; } else{ /* BUG: no "exceptions file" support */ apval = (vtmp->is_changed_val ? &vtmp->changed_val.p : APVAL(&ps_global->vars[V_LITERAL_SIG], Main)); if(apval){ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char)); sig[0] = '\0'; cstring_to_string(*apval, sig); } else err = "Problem accessing configuration"; } } else if((vtmp = &ps_global->vars[V_SIGNATURE_FILE]) && !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p : ps_global->VAR_SIGNATURE_FILE)) snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s", vtmp->is_changed_val ? (vtmp->changed_val.p ? vtmp->changed_val.p : "") : (ps_global->VAR_SIGNATURE_FILE ? ps_global->VAR_SIGNATURE_FILE : "")); else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val ? vtmp->changed_val.p : ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))) err = "Can't read remote pinerc"; if(err){ Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } if(peTSig){ for(i = 0; peTSig[i]; i++) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(peTSig[i],-1)); } else { for(p = sig; (q = strindex(p, '\n')); p = q + 1) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, q - p)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(p, -1)); fs_give((void **) &sig); } return(TCL_OK); } else if(!strcmp(s1, "filters")){ long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED; PAT_STATE pstate; PAT_S *pat; close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(pat->patgrp->nick, -1)); } } return(TCL_OK); } else if(!strcmp(s1, "scores")){ long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED; PAT_STATE pstate; PAT_S *pat; close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(pat->patgrp->nick, -1)); } } return(TCL_OK); } else if(!strcmp(s1, "indexcolors")){ long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED; PAT_STATE pstate; PAT_S *pat; close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(pat->patgrp->nick, -1)); } } return(TCL_OK); } else if(!strcmp(s1, "collections")){ struct variable *vtmp; int i; CONTEXT_S *new_ctxt; vtmp = &ps_global->vars[V_FOLDER_SPEC]; for(i = 0; (vtmp->is_changed_val ? vtmp->changed_val.l && vtmp->changed_val.l[i] : vtmp->current_val.l && vtmp->current_val.l[i]); i++){ new_ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[i] : vtmp->current_val.l[i], NULL); peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", new_ctxt->nickname ? new_ctxt->nickname : (new_ctxt->server ? new_ctxt->server : (new_ctxt->label ? new_ctxt->label : "Some Collection")), new_ctxt->label ? new_ctxt->label : ""); free_context(&new_ctxt); } vtmp = &ps_global->vars[V_NEWS_SPEC]; for(i = 0; (vtmp->is_changed_val ? vtmp->changed_val.l && vtmp->changed_val.l[i] : vtmp->current_val.l && vtmp->current_val.l[i]); i++){ new_ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[i] : vtmp->current_val.l[i], NULL); peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", new_ctxt->nickname ? new_ctxt->nickname : (new_ctxt->server ? new_ctxt->server : (new_ctxt->label ? new_ctxt->label : "Some Collection")), new_ctxt->label ? new_ctxt->label : ""); free_context(&new_ctxt); } return(TCL_OK); } else if(!strcmp(s1, "newconf")){ struct variable *vtmp; int i; FEATURE_S *feature; for(vtmp = ps_global->vars; vtmp->name; vtmp++) vtmp->is_changed_val = 0; for(i = 0; (feature = feature_list(i)); i++) F_CH_SET(feature->id, F_ON(feature->id, ps_global)); if(peTSig){ for(i = 0; peTSig[i]; i++) fs_give((void **)&peTSig[i]); fs_give((void **)&peTSig); } close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED); return(TCL_OK); } else if(!strcmp(s1, "saveconf")){ struct variable *vtmp; int i, did_change = 0, def_sort_rev; FEATURE_S *feature; if(ps_global->vars[V_FEATURE_LIST].is_changed_val){ ps_global->vars[V_FEATURE_LIST].is_changed_val = 0; for(i = 0; (feature = feature_list(i)); i++) if(feature_list_section(feature)){ if(F_CH_ON(feature->id) != F_ON(feature->id, ps_global)){ did_change = 1; toggle_feature(ps_global, &ps_global->vars[V_FEATURE_LIST], feature, TRUE, Main); } } } for(vtmp = ps_global->vars; vtmp->name; vtmp++){ if(vtmp->is_changed_val && (vtmp - ps_global->vars != V_FEATURE_LIST)){ if(vtmp->is_list){ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++) fs_give((void **)&vtmp->main_user_val.l[i]); if(vtmp->main_user_val.l) fs_give((void **)&vtmp->main_user_val.l); vtmp->main_user_val.l = vtmp->changed_val.l; vtmp->changed_val.l = NULL; } else { if(vtmp->main_user_val.p) fs_give((void **)&vtmp->main_user_val.p); vtmp->main_user_val.p = vtmp->changed_val.p; vtmp->changed_val.p = NULL; } set_current_val(vtmp, FALSE, FALSE); vtmp->is_changed_val = 0; did_change = 1; switch (vtmp - ps_global->vars) { case V_USER_DOMAIN: init_hostname(ps_global); case V_FOLDER_SPEC: case V_NEWS_SPEC: free_contexts(&ps_global->context_list); init_folders(ps_global); break; case V_NORM_FORE_COLOR: pico_set_fg_color(vtmp->current_val.p); break; case V_NORM_BACK_COLOR: pico_set_bg_color(vtmp->current_val.p); break; case V_ADDRESSBOOK: case V_GLOB_ADDRBOOK: #ifdef ENABLE_LDAP case V_LDAP_SERVERS: #endif case V_ABOOK_FORMATS: addrbook_reset(); case V_INDEX_FORMAT: init_index_format(ps_global->VAR_INDEX_FORMAT, &ps_global->index_disp_format); clear_index_cache(sp_inbox_stream(), 0); break; case V_PAT_FILTS: close_patterns(ROLE_DO_FILTER | PAT_USE_CURRENT); role_process_filters(); break; case V_PAT_INCOLS: close_patterns(ROLE_DO_INCOLS | PAT_USE_CURRENT); clear_index_cache(sp_inbox_stream(), 0); role_process_filters(); break; case V_PAT_SCORES: close_patterns(ROLE_DO_SCORES | PAT_USE_CURRENT); role_process_filters(); break; case V_DEFAULT_FCC: case V_DEFAULT_SAVE_FOLDER: init_save_defaults(); break; case V_SORT_KEY: decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev); break; case V_VIEW_HDR_COLORS : set_custom_spec_colors(ps_global); break; case V_POST_CHAR_SET : update_posting_charset(ps_global, 1); break; default: break; } } } if(peTSig){ peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE, NULL); } if(did_change){ if(write_pinerc(ps_global, Main, WRP_NOUSER) == 0) q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!"); } return(TCL_OK); } else if(!strcmp(s1, "columns")){ Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "indextokens")){ INDEX_PARSE_T *tok; int i; for(i = 0; (tok = itoken(i)) != NULL; i++) if(tok->what_for & FOR_INDEX) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tok->name, -1)); return(TCL_OK); } } else if(objc == 3){ if(!strcmp(s1, "varget")){ char *varname = Tcl_GetStringFromObj(objv[2], NULL); struct variable *vtmp; Tcl_Obj *resObj, *secObj; char *input_type; int is_default, i; NAMEVAL_S *tmpnv; if(varname == NULL) return(TCL_ERROR); for(vtmp = ps_global->vars; vtmp->name && strucmp(vtmp->name, varname); vtmp++) ; if(!vtmp->name){ Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } resObj = Tcl_NewListObj(0, NULL); if(vtmp->is_list){ if(vtmp->is_changed_val){ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++){ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(vtmp->changed_val.l[i], -1)); } } else { for(i = 0; vtmp->current_val.l && vtmp->current_val.l[i]; i++){ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(vtmp->current_val.l[i], -1)); } } } else { if(vtmp->is_changed_val){ if(vtmp->changed_val.p) Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(vtmp->changed_val.p[0] ? vtmp->changed_val.p : "\"\"", -1)); } else { if(vtmp->current_val.p) Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(vtmp->current_val.p[0] ? vtmp->current_val.p : "\"\"", -1)); } } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); secObj = Tcl_NewListObj(0, NULL); if(vtmp->is_list) input_type = cpystr("textarea"); else{ NAMEVAL_S *(*tmpf)(int); switch(vtmp - ps_global->vars){ case V_SAVED_MSG_NAME_RULE: tmpf = save_msg_rules; break; case V_FCC_RULE: tmpf = fcc_rules; break; case V_SORT_KEY: tmpf = sort_key_rules; break; case V_AB_SORT_RULE: tmpf = ab_sort_rules; break; case V_FLD_SORT_RULE: tmpf = fld_sort_rules; break; case V_GOTO_DEFAULT_RULE: tmpf = goto_rules; break; case V_INCOMING_STARTUP: tmpf = incoming_startup_rules; break; case V_PRUNING_RULE: tmpf = pruning_rules; break; case V_WP_INDEXHEIGHT: tmpf = wp_indexheight_rules; break; default: tmpf = NULL; break; } if(tmpf){ for(i = 0; (tmpnv = (tmpf)(i)); i++){ if(tmpnv->shortname) peAppListF(interp, secObj, "%s%s", tmpnv->name, tmpnv->shortname); else Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(tmpnv->name, -1)); } input_type = cpystr("listbox"); } else input_type = cpystr("text"); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(input_type, -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj); if(vtmp->is_list) is_default = !vtmp->is_changed_val && !vtmp->main_user_val.l; else is_default = !vtmp->is_changed_val && !vtmp->main_user_val.p; Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(is_default)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(vtmp->is_fixed)); return(TCL_OK); } else if(!strcmp(s1, "filtextended")){ int fl, i; long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED; PAT_STATE pstate; PAT_S *pat; Tcl_Obj *resObj = NULL, *tObj = NULL; if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR) return(TCL_ERROR); close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate), i = 0; pat && i != fl; pat = next_pattern(&pstate), i++); if(!pat) return(TCL_ERROR); /* append the pattern ID */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1)); pePatAppendID(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* append the pattern */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1)); pePatAppendPattern(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* now append the filter action */ resObj = Tcl_NewListObj(0, NULL); peAppListF(interp, resObj, "%s%i", "kill", pat->action->folder ? 0 : 1); peAppListF(interp, resObj, "%s%p", "folder", pat->action->folder); peAppListF(interp, resObj, "%s%i", "move_only_if_not_deleted", pat->action->move_only_if_not_deleted); tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("filtaction", -1)); Tcl_ListObjAppendElement(interp, tObj, resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); } else return(TCL_ERROR); return(TCL_OK); } else if(!strcmp(s1, "indexcolorextended")){ int fl, i; long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED; PAT_STATE pstate; PAT_S *pat; Tcl_Obj *resObj = NULL, *tObj = NULL; if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR) return(TCL_ERROR); close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate), i = 0; pat && i != fl; pat = next_pattern(&pstate), i++); if(!pat) return(TCL_ERROR); /* append the pattern ID */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1)); pePatAppendID(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* append the pattern */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1)); pePatAppendPattern(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* now append the pattern colors */ resObj = Tcl_NewListObj(0, NULL); tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolor", -1)); if(pat->action->is_a_incol){ char *color; Tcl_Obj *colObj = Tcl_NewListObj(0, NULL); if(!(pat->action->incol && pat->action->incol->fg && pat->action->incol->fg[0] && (color = color_to_asciirgb(pat->action->incol->fg)) && (color = peColorStr(color,tmp_20k_buf)))) color = ""; Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1)); if(!(pat->action->incol && pat->action->incol->bg && pat->action->incol->bg[0] && (color = color_to_asciirgb(pat->action->incol->bg)) && (color = peColorStr(color,tmp_20k_buf)))) color = ""; Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1)); Tcl_ListObjAppendElement(interp, tObj, colObj); } Tcl_ListObjAppendElement(interp, resObj, tObj); tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolors", -1)); Tcl_ListObjAppendElement(interp, tObj, resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); } else return(TCL_ERROR); return(TCL_OK); } else if(!strcmp(s1, "scoreextended")){ int fl, i; long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED; char *hdr = NULL; PAT_STATE pstate; PAT_S *pat; Tcl_Obj *resObj = NULL, *tObj = NULL; if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR) return(TCL_ERROR); close_every_pattern(); if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate), i = 0; pat && i != fl; pat = next_pattern(&pstate), i++); if(!pat) return(TCL_ERROR); /* append the pattern ID */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1)); pePatAppendID(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* append the pattern */ tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1)); pePatAppendPattern(interp, tObj, pat); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* now append the filter action */ resObj = Tcl_NewListObj(0, NULL); peAppListF(interp, resObj, "%s%l", "scoreval", pat->action->scoreval); if(pat->action->scorevalhdrtok) hdr = hdrtok_to_stringform(pat->action->scorevalhdrtok); peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : ""); if(hdr) fs_give((void **) &hdr); tObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("scores", -1)); Tcl_ListObjAppendElement(interp, tObj, resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); } else return(TCL_ERROR); return(TCL_OK); } else if(!strcmp(s1, "clextended")){ int cl, i, j = 0, in_folder_spec = 0; struct variable *vtmp; char tpath[MAILTMPLEN], *p; CONTEXT_S *ctxt; vtmp = &ps_global->vars[V_FOLDER_SPEC]; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR) return(TCL_ERROR); for(i = 0; i < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i])); i++); if(i == cl && (vtmp->is_changed_val ? vtmp->changed_val.l && vtmp->changed_val.l[i] : vtmp->current_val.l && vtmp->current_val.l[i])) in_folder_spec = 1; else { vtmp = &ps_global->vars[V_NEWS_SPEC]; for(j = 0; i + j < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j])); j++); } if(in_folder_spec || (i + j == cl && (vtmp->is_changed_val ? vtmp->changed_val.l && vtmp->changed_val.l[j] : vtmp->current_val.l && vtmp->current_val.l[j]))){ ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[in_folder_spec ? i : j] : vtmp->current_val.l[in_folder_spec ? i : j], NULL); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ctxt->nickname ? ctxt->nickname : "", -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ctxt->label ? ctxt->label : "", -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ctxt->server ? ctxt->server : "", -1)); tpath[0] = '\0'; if(ctxt->context){ strncpy(tpath, (ctxt->context[0] == '{' && (p = strchr(ctxt->context, '}'))) ? ++p : ctxt->context, sizeof(tpath)); tpath[sizeof(tpath)-1] = '\0'; if((p = strstr(tpath, "%s")) != NULL) *p = '\0'; } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tpath, -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ctxt->dir && ctxt->dir->view.user ? ctxt->dir->view.user : "", -1)); free_context(&ctxt); return(TCL_OK); } else return(TCL_ERROR); } else if(!strcmp(s1, "rawsig")){ struct variable *vtmp; char *cstring_version, *sig, *line; int i, nSig; Tcl_Obj **objSig; vtmp = &ps_global->vars[V_LITERAL_SIG]; if(vtmp->is_changed_val ? vtmp->changed_val.p : ps_global->VAR_LITERAL_SIG){ tmp_20k_buf[0] = '\0'; Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig); for(i = 0; i < nSig && i < SIG_MAX_LINES; i++) if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL) snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line); sig = cpystr(tmp_20k_buf); if((cstring_version = string_to_cstring(sig)) != NULL){ if(vtmp->changed_val.p) fs_give((void **)&vtmp->changed_val.p); vtmp->is_changed_val = 1; vtmp->changed_val.p = cstring_version; } fs_give((void **) &sig); return(TCL_OK); } else { if(peTSig){ for(i = 0; peTSig[i]; i++) fs_give((void **)&peTSig[i]); fs_give((void **)&peTSig); } Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig); peTSig = (char **)fs_get(sizeof(char)*(nSig + 1)); for(i = 0; i < nSig; i++){ line = Tcl_GetStringFromObj(objSig[i], NULL); peTSig[i] = cpystr(line ? line : ""); } peTSig[i] = NULL; return(TCL_OK); } } else if(!strcmp(s1, "colorget")){ char *varname; char tvname[256], hexcolor[256]; struct variable *vtmp; if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){ return(TCL_ERROR); } if(strcmp("viewer-hdr-colors", varname) == 0){ SPEC_COLOR_S *hcolors, *thc; Tcl_Obj *resObj; char hexcolor[256], *tstr = NULL; if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val) hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0); else hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0); for(thc = hcolors; thc; thc = thc->next){ resObj = Tcl_NewListObj(0,NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(thc->spec, -1)); hex_colorstr(hexcolor, thc->fg); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(hexcolor, -1)); hex_colorstr(hexcolor, thc->bg); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(hexcolor, -1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(thc->val ? tstr = pattern_to_string(thc->val) : "", -1)); if(tstr) fs_give((void **)&tstr); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); } fs_give((void **)&hcolors); return(TCL_OK); } else { char *colorp; snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color"); for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name) return(TCL_ERROR); if(vtmp->is_list) return(TCL_ERROR); colorp = (vtmp->is_changed_val && vtmp->changed_val.p) ? vtmp->changed_val.p : (vtmp->current_val.p) ? vtmp->current_val.p : vtmp->global_val.p; if(colorp){ hex_colorstr(hexcolor, colorp); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(hexcolor, -1)); } else Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("", -1)); snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color"); vtmp++; if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name) for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR]; vtmp->name && strucmp(vtmp->name, tvname); vtmp++) ; if(!vtmp->name) return(TCL_ERROR); if(vtmp->is_list) return(TCL_ERROR); colorp = (vtmp->is_changed_val && vtmp->changed_val.p) ? vtmp->changed_val.p : (vtmp->current_val.p) ? vtmp->current_val.p : vtmp->global_val.p; if(colorp){ hex_colorstr(hexcolor, colorp); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(hexcolor, -1)); } else Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("", -1)); } return(TCL_OK); } else if(!strcmp(s1, "cldel")){ int cl, i, j, n; struct variable *vtmp; char **newl; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR) return(TCL_ERROR); vtmp = &ps_global->vars[V_FOLDER_SPEC]; for(i = 0; i < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i])); i++); if(!(i == cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i])))){ vtmp = &ps_global->vars[V_NEWS_SPEC]; for(j = 0; i + j < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j])); j++); if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j]))) return(TCL_ERROR); i = j; } for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n]) : (vtmp->current_val.l && vtmp->current_val.l[n]); n++); newl = (char **)fs_get(n*(sizeof(char *))); for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n]) : (vtmp->current_val.l && vtmp->current_val.l[n]); n++){ if(n < i) newl[n] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n] : vtmp->current_val.l[n]); else if(n > i) newl[n-1] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n] : vtmp->current_val.l[n]); } newl[n-1] = NULL; vtmp->is_changed_val = 1; for(n = 0; vtmp->changed_val.l && vtmp->changed_val.l[n]; n++) fs_give((void **) &vtmp->changed_val.l[n]); if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l); vtmp->changed_val.l = newl; return(TCL_OK); } else if(!strcmp(s1, "columns")){ int n; char *p; if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR && n >= MIN_SCREEN_COLS && n < (MAX_SCREEN_COLS - 1) && ps_global->ttyo->screen_cols != n){ clear_index_cache(sp_inbox_stream(), 0); ps_global->ttyo->screen_cols = n; set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main); Tcl_SetResult(interp, p, TCL_VOLATILE); } else Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "reset")){ char *p; if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(!strcmp(p,"pinerc")){ struct variable *var; PINERC_S *prc; /* new pinerc structure, copy location pointers */ prc = new_pinerc_s(ps_global->prc->name); prc->type = ps_global->prc->type; prc->rd = ps_global->prc->rd; prc->outstanding_pinerc_changes = 1; /* tie off original pinerc struct and free it */ ps_global->prc->rd = NULL; ps_global->prc->outstanding_pinerc_changes = 0; free_pinerc_s(&ps_global->prc); /* set global->prc to new struct with no pinerc_lines * and fool write_pinerc into not writing changed vars */ ps_global->prc = prc; /* * write at least one var into nearly empty pinerc * and clear user's var settings. clear global cause * they'll get reset in peInitVars */ for(var = ps_global->vars; var->name != NULL; var++){ var->been_written = ((var - ps_global->vars) != V_LAST_VERS_USED); if(var->is_list){ free_list_array(&var->main_user_val.l); free_list_array(&var->global_val.l); } else{ fs_give((void **)&var->main_user_val.p); fs_give((void **)&var->global_val.p); } } write_pinerc(ps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN); peInitVars(ps_global); return(TCL_OK); } } } } else if(objc == 4){ if(!strcmp(s1, "varset")){ char *varname = Tcl_GetStringFromObj(objv[2], NULL); struct variable *vtmp; char **tstrlist = NULL, *line, *tline; Tcl_Obj **objVal; int i, strlistpos, numlistvals; if(varname == NULL) return(TCL_ERROR); for(vtmp = ps_global->vars; vtmp->name && strucmp(vtmp->name, varname); vtmp++) ; if(!vtmp->name){ Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } if(Tcl_ListObjGetElements(interp, objv[3], &numlistvals, &objVal) != TCL_OK) return(TCL_ERROR); vtmp->is_changed_val = 1; if(vtmp->is_list){ if(vtmp->changed_val.l){ for(i = 0; vtmp->changed_val.l[i]; i++) fs_give((void **)&vtmp->changed_val.l[i]); fs_give((void **)&vtmp->changed_val.l); } if(numlistvals) tstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *)); for(i = 0, strlistpos = 0; i < numlistvals; i++){ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){ tline = cpystr(line); removing_leading_and_trailing_white_space(tline); if(*tline) tstrlist[strlistpos++] = cpystr(tline); fs_give((void **) &tline); } } if(tstrlist) tstrlist[strlistpos] = NULL; vtmp->changed_val.l = tstrlist; } else { if(vtmp->changed_val.p) fs_give((void **)&vtmp->changed_val.p); if(numlistvals){ if((line = Tcl_GetStringFromObj(objVal[0], 0)) != NULL){ tline = cpystr(line); if(strucmp(vtmp->name, "reply-indent-string")) removing_leading_and_trailing_white_space(tline); if(!strcmp(tline, "\"\"")){ tline[0] = '\0'; } else if(tline[0] == '\0'){ fs_give((void **)&tline); } if(tline){ vtmp->changed_val.p = cpystr(tline); fs_give((void **)&tline); } } else vtmp->changed_val.p = cpystr(""); } } return(TCL_OK); } else if(!strcmp(s1, "feature")){ char *featurename; int i, set, wasset = 0; FEATURE_S *feature; /* * CMD: feature * * ARGS: featurename - * value - new value to assign flag * * Returns: 1 if named feature set, 0 otherwise * */ if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR) for(i = 0; (feature = feature_list(i)); i++) if(!strucmp(featurename, feature->name)){ ps_global->vars[V_FEATURE_LIST].is_changed_val = 1; wasset = F_CH_ON(feature->id); F_CH_SET(feature->id, set); break; } Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "clshuff")){ char *dir, *tstr, **newl; int cl, up = 0, fvarn, nvarn, icnt, i; struct variable *fvar, *nvar, *vtmp; if(!(dir = Tcl_GetStringFromObj(objv[2], NULL))) return TCL_ERROR; if(Tcl_GetIntFromObj(interp, objv[3], &cl) == TCL_ERROR) return(TCL_ERROR); if(!strcmp(dir, "up")) up = 1; else if(!strcmp(dir, "down")) up = 0; else return(TCL_ERROR); fvar = &ps_global->vars[V_FOLDER_SPEC]; nvar = &ps_global->vars[V_NEWS_SPEC]; for(fvarn = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[fvarn]) : (fvar->current_val.l && fvar->current_val.l[fvarn]); fvarn++); for(nvarn = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[nvarn]) : (nvar->current_val.l && nvar->current_val.l[nvarn]); nvarn++); if(cl < fvarn){ vtmp = fvar; icnt = cl; } else if(cl >= fvarn && cl < nvarn + fvarn){ vtmp = nvar; icnt = cl - fvarn; } else return(TCL_ERROR); if(vtmp == nvar && icnt == 0 && up){ newl = (char **)fs_get((fvarn + 2)*sizeof(char *)); for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i]) : (fvar->current_val.l && fvar->current_val.l[i]); i++) newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i] : fvar->current_val.l[i]); newl[i++] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[0] : nvar->current_val.l[0]); newl[i] = NULL; fvar->is_changed_val = 1; for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++) fs_give((void **)&fvar->changed_val.l[i]); if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l); fvar->changed_val.l = newl; newl = (char **)fs_get(nvarn*sizeof(char *)); for(i = 1; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i]) : (nvar->current_val.l && nvar->current_val.l[i]); i++) newl[i-1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i] : nvar->current_val.l[i]); newl[i-1] = NULL; nvar->is_changed_val = 1; for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++) fs_give((void **)&nvar->changed_val.l[i]); if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l); nvar->changed_val.l = newl; vtmp = fvar; icnt = fvarn; } else if(vtmp == fvar && icnt == fvarn - 1 && !up){ newl = (char **)fs_get(fvarn*sizeof(char *)); for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i+1]) : (fvar->current_val.l && fvar->current_val.l[i+1]); i++) newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i] : fvar->current_val.l[i]); newl[i] = NULL; tstr = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i] : fvar->current_val.l[i]); fvar->is_changed_val = 1; for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++) fs_give((void **)&fvar->changed_val.l[i]); if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l); fvar->changed_val.l = newl; newl = (char **)fs_get((nvarn+2)*sizeof(char *)); newl[0] = tstr; for(i = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i]) : (nvar->current_val.l && nvar->current_val.l[i]); i++) newl[i+1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i] : nvar->current_val.l[i]); newl[i+1] = NULL; nvar->is_changed_val = 1; for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++) fs_give((void **)&nvar->changed_val.l[i]); if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l); nvar->changed_val.l = newl; vtmp = nvar; icnt = 0; } else { newl = (char **)fs_get(((vtmp == fvar ? fvarn : nvarn) + 1)*sizeof(char *)); for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i]); i++) newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i] : vtmp->current_val.l[i]); newl[i] = NULL; vtmp->is_changed_val = 1; for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++) fs_give((void **)&vtmp->changed_val.l[i]); if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l); vtmp->changed_val.l = newl; } if(up){ tstr = vtmp->changed_val.l[icnt-1]; vtmp->changed_val.l[icnt-1] = vtmp->changed_val.l[icnt]; vtmp->changed_val.l[icnt] = tstr; } else { tstr = vtmp->changed_val.l[icnt+1]; vtmp->changed_val.l[icnt+1] = vtmp->changed_val.l[icnt]; vtmp->changed_val.l[icnt] = tstr; } return(TCL_OK); } } else if(objc == 7){ if(!strcmp(s1, "cledit") || !strcmp(s1, "cladd")){ int add = 0, cl, quotes_needed = 0, i, j, newn; char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4]; char **newl; struct variable *vtmp; if(!strcmp(s1, "cladd")) add = 1; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR) return(TCL_ERROR); if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))) return TCL_ERROR; if(!(server = Tcl_GetStringFromObj(objv[4], NULL))) return TCL_ERROR; if(!(path = Tcl_GetStringFromObj(objv[5], NULL))) return TCL_ERROR; if(!(view = Tcl_GetStringFromObj(objv[6], NULL))) return TCL_ERROR; removing_leading_and_trailing_white_space(nick); removing_leading_and_trailing_white_space(server); removing_leading_and_trailing_white_space(path); removing_leading_and_trailing_white_space(view); if(strchr(nick, ' ')) quotes_needed = 1; if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) > MAILTMPLEN * 4 - 20) { /* for good measure */ Tcl_SetResult(interp, "info too long", TCL_VOLATILE); return TCL_ERROR; } if(3 + strlen(nick) + strlen(server) + strlen(path) + strlen(view) > MAILTMPLEN + 4){ Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE); return(TCL_OK); } snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ? "\"" : "", nick, quotes_needed ? "\"" : "", strlen(nick) ? " " : "", server, path, view); if(add) { vtmp = &ps_global->vars[V_NEWS_SPEC]; if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[0]) : (vtmp->current_val.l && vtmp->current_val.l[0]))) vtmp = &ps_global->vars[V_FOLDER_SPEC]; for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i]); i++); newn = i + 1; newl = (char **)fs_get((newn + 1)*sizeof(char *)); for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i]); i++) newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i] : vtmp->current_val.l[i]); newl[i++] = cpystr(context_buf); newl[i] = NULL; } else { vtmp = &ps_global->vars[V_FOLDER_SPEC]; for(i = 0; i < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i])); i++); if(!(i == cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i]) : (vtmp->current_val.l && vtmp->current_val.l[i])))){ vtmp = &ps_global->vars[V_NEWS_SPEC]; for(j = 0; i + j < cl && (vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j])); j++); if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j]))) return(TCL_ERROR); i = j; } for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j]); j++); newl = (char **)fs_get(j * sizeof(char *)); for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j]) : (vtmp->current_val.l && vtmp->current_val.l[j]); j++){ if(j == i) newl[j] = cpystr(context_buf); else newl[j] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[j] : vtmp->current_val.l[j]); } newl[j] = NULL; } vtmp->is_changed_val = 1; for(j = 0; vtmp->changed_val.l && vtmp->changed_val.l[j]; j++) fs_give((void **)&vtmp->changed_val.l[j]); if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l); vtmp->changed_val.l = newl; return TCL_OK; } } else err = "PEInfo: Too many arguments"; } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } int peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv) { int try_cache, e, i, n, nSig; char datebuf[200], *sig, *line; FILE *fp; REMDATA_S *rd; Tcl_Obj **objSig; if(!(file && IS_REMOTE(file))){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s", file ? file : ""); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } /* * We could parse the name here to find what type it is. So far we * only have type RemImap. */ rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE, NULL, "Error: ", "Can't fetch remote signature."); if(!rd){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } try_cache = rd_read_metadata(rd); if(rd->access == MaybeRorW){ if(rd->read_status == 'R') rd->access = ReadOnly; else rd->access = ReadWrite; } if(rd->access != NoExists){ rd_check_remvalid(rd, 1L); /* * If the cached info says it is readonly but * it looks like it's been fixed now, change it to readwrite. */ if(rd->read_status == 'R'){ /* * We go to this trouble since readonly sigfiles * are likely a mistake. They are usually supposed to be * readwrite so we open it and check if it's been fixed. */ rd_check_readonly_access(rd); if(rd->read_status == 'W'){ rd->access = ReadWrite; rd->flags |= REM_OUTOFDATE; } else{ rd_close_remdata(&rd); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } } if(rd->flags & REM_OUTOFDATE){ if(rd_update_local(rd) != 0){ dprint((1, "pinerc_remote_open: rd_update_local failed")); /* * Don't give up altogether. We still may be * able to use a cached copy. */ } else{ dprint((7, "%s: copied remote to local (%ld)", rd->rn, (long)rd->last_use)); } } if(rd->access == ReadWrite) rd->flags |= DO_REMTRIM; } /* If we couldn't get to remote folder, try using the cached copy */ if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){ rd_close_remdata(&rd); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } unlink(rd->lf); sig = NULL; tmp_20k_buf[0] = '\0'; if(objv){ Tcl_ListObjGetElements(interp, objv[0], &nSig, &objSig); for(i = 0; i < nSig && i < SIG_MAX_LINES; i++){ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL) snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line); } } else if(peTSig){ for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) { snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, peTSig[i]); } for(i = 0; peTSig[i]; i++) fs_give((void **)&peTSig[i]); fs_give((void **)&peTSig); } else return(TCL_ERROR); sig = cpystr(tmp_20k_buf); if((fp = fopen(rd->lf, "w")) != NULL) n = fwrite(sig, strlen(sig), 1, fp); fs_give((void **) &sig); if(fp){ if(n != 1){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s", rd->lf, error_description(errno)); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); rd_close_remdata(&rd); } fclose(fp); if(n != 1) return(TCL_ERROR); } else { rd_close_remdata(&rd); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s", rd->lf, error_description(errno)); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } datebuf[0] = '\0'; if(!rd->t.i.stream){ long retflags = 0; rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags); } if((e = rd_update_remote(rd, datebuf)) != 0){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s", rd->lf, error_description(errno)); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); rd_close_remdata(&rd); return(TCL_ERROR); } rd_update_metadata(rd, datebuf); rd->read_status = 'W'; rd_close_remdata(&rd); return(TCL_OK); } NAMEVAL_S *sort_key_rules(index) int index; { static NAMEVAL_S is_rules[] = { {"Arrival", 0}, {"Date", 0}, {"Subject", 0}, {"Cc", 0}, {"From", 0}, {"To", 0}, {"size", 0}, {"OrderedSubj", 0}, {"tHread", 0}, {"Arrival/Reverse", 0}, {"Date/Reverse", 0}, {"Subject/Reverse", 0}, {"Cc/Reverse", 0}, {"From/Reverse", 0}, {"To/Reverse", 0}, {"size/Reverse", 0}, {"tHread/Reverse", 0}, {"OrderedSubj/Reverse", 0} }; return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0]))) ? &is_rules[index] : NULL); } NAMEVAL_S *wp_indexheight_rules(index) int index; { static NAMEVAL_S is_rules[] = { {"normal font", "24", 0}, {"smallest font", "20", 0}, {"small font", "22", 0}, {"large font", "28", 0}, {"largest font", "30", 0} }; return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0]))) ? &is_rules[index] : NULL); } /* * PEDebugCmd - turn on/off and set various debugging options */ int PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *s; if(!--objc){ /* only one arg? */ Tcl_WrongNumArgs(interp, 1, objv, "?args?"); } else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ if(!strucmp(s, "level")){ if(objc == 2){ int level; if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) return(TCL_ERROR); if(level > 0){ if(level > 10) level = 10; debug = level; dprint((1, "Debug level %d", level)); } else{ dprint((1, "PEDebug ending")); debug = 0; } } Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(s, "write")){ if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){ /* * script debugging has a high priority since * statements can be added/removed on the fly * AND are NOT present by default */ dprint((SYSDBG_INFO, "SCRIPT: %s", s)); } return(TCL_OK); } else if(!strucmp(s, "imap")){ int level; if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) return(TCL_ERROR); if(level == 0){ if(ps_global){ ps_global->debug_imap = 0; if(ps_global->mail_stream) mail_nodebug(ps_global->mail_stream); } } else if(level > 0 && level < 5){ if(ps_global){ ps_global->debug_imap = level; if(ps_global->mail_stream) mail_debug(ps_global->mail_stream); } } return(TCL_OK); } else Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC); } return(TCL_ERROR); } /* * PESessionCmd - Export TCL Session-wide command set */ int PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *op, *err = "Unknown PESession option"; char *pe_user, *pe_host; int pe_alt, l; dprint((2, "PESessionCmd")); if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ if(!strcmp(op, "open")){ char *s, *pinerc, *pineconf = NULL; /* * CMD: open user remote-pinerc local-default-config * * Initiate a session * * Returns: error string on error, nothing otherwise */ if(objc < 4 || objc > 5){ Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc"); return(TCL_ERROR); } if(!(s = Tcl_GetStringFromObj(objv[2], &l))){ Tcl_SetResult(interp, "Unknown User", TCL_STATIC); return(TCL_ERROR); } else{ int rv; pe_user = cpystr(s); #if defined(HAVE_SETENV) rv = setenv("WPUSER", pe_user, 1); #elif defined(HAVE_PUTENV) { static char putenvbuf[PUTENV_MAX]; if(l + 8 < PUTENV_MAX){ if(putenvbuf[0]) /* only called once, but you never know */ snprintf(putenvbuf + 7, PUTENV_MAX - 7, "%s", pe_user); else snprintf(putenvbuf, PUTENV_MAX, "WPUSER=%s", pe_user); rv = putenv(putenvbuf); } else rv = 1; } #endif if(rv){ fs_give((void **) &pe_user); Tcl_SetResult(interp, (errno == ENOMEM) ? "Insufficient Environment Space" : "Cannot set WPUSER in environment", TCL_STATIC); return(TCL_ERROR); } } if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ NETMBX mb; if(mail_valid_net_parse(pinerc, &mb)){ pe_host = cpystr(mb.host); pe_alt = (mb.sslflag || mb.tlsflag); } else { snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } } else { Tcl_SetResult(interp, "Unknown config location", TCL_STATIC); return(TCL_ERROR); } if(objc == 5 && !(pineconf = Tcl_GetStringFromObj(objv[4], NULL))){ Tcl_SetResult(interp, "Can't determine global config", TCL_STATIC); return(TCL_ERROR); } dprint((SYSDBG_INFO, "session (%s) %s - %s", pe_user, pinerc, pineconf ? pineconf : "")); /* credential cache MUST already be seeded */ /* destroy old user context */ if(ps_global){ /* destroy open stream */ peDestroyStream(ps_global); /* destroy old user context */ peDestroyUserContext(&ps_global); } /* Establish a user context */ if((s = peCreateUserContext(interp, pe_user, pinerc, pineconf)) != NULL){ Tcl_SetResult(interp, s, TCL_VOLATILE); return(TCL_ERROR); } fs_give((void **) &pe_user); fs_give((void **) &pe_host); return(TCL_OK); } else if(!strcmp(op, "close")){ if(ps_global){ /* destroy any open stream */ peDestroyStream(ps_global); /* destroy user context */ peDestroyUserContext(&ps_global); } Tcl_SetResult(interp, "BYE", TCL_STATIC); return(TCL_OK); } else if(!strcmp(op, "creds")){ char *folder; int colid; if(objc < 4){ err = "creds: insufficient args"; } else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR && (folder = Tcl_GetStringFromObj(objv[3], NULL))){ int i; CONTEXT_S *cp; /* * CMD: creds [user passwd] * * Test for valid credentials to access given folder * * Returns: 1 if so, 0 otherwise */ for(i = 0, cp = ps_global ? ps_global->context_list : NULL; i < 1 || cp != NULL ; i++, cp = cp->next) if(i == colid){ int rv = 0; char tmp[MAILTMPLEN], *p; if(cp){ if(folder[0] == '\0'){ if(cp->use & CNTXT_INCMNG) rv = 1; else folder = "fake-fake"; } else if((cp->use & CNTXT_INCMNG) && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE))) folder = p; } if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){ NETMBX mb; if(mail_valid_net_parse(tmp, &mb)){ if(objc == 4){ /* check creds */ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag)))) strcpy(mb.user, p); if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag))) rv = 1; } else if(objc == 6){ /* set creds */ char *user, *passwd; if((user = Tcl_GetStringFromObj(objv[4], NULL)) && (passwd = Tcl_GetStringFromObj(objv[5], NULL))){ if(*mb.user && strcmp(mb.user, user)){ err = "creds: mismatched user names"; break; } alpine_set_passwd(user, passwd, mb.host, mb.sslflag || mb.tlsflag || (ps_global ? F_ON(F_PREFER_ALT_AUTH, ps_global) : 0)); rv = 1; } else { err = "creds: unable to read credentials"; break; } } else{ err = "creds: invalid args"; break; } } } (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(rv)); return(TCL_OK); } err = "creds: Unrecognized collection ID"; } else err = "creds: failure to acquire folder and collection ID"; } else if(!strcmp(op, "nocred")){ char *folder; int colid; if(!ps_global){ err = "No Session active"; } else if(objc != 4){ err = "nocred: wrong number of args"; } else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR && (folder = Tcl_GetStringFromObj(objv[3], NULL))){ int i; CONTEXT_S *cp; /* * CMD: nocred * * Test for valid credentials to access given folder * * Returns: 1 if so, 0 otherwise */ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ int rv = 0; char tmp[MAILTMPLEN], *p; if((cp->use & CNTXT_INCMNG) && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE))) folder = p; if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){ NETMBX mb; if(mail_valid_net_parse(tmp, &mb)){ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag)))) strcpy(mb.user, p); alpine_clear_passwd(mb.user, mb.host); } } (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(rv)); return(TCL_OK); } err = "creds: Unrecognized collection ID"; } else err = "creds: failure to acquire folder and collection ID"; } else if(!strcmp(op, "acceptcert")){ char *certhost; STRLIST_S **p; if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){ for(p = &peCertHosts; *p; p = &(*p)->next) ; *p = new_strlist(certhost); } err = "PESession: no server name"; } else if(!strcmp(op, "random")){ if(objc != 3){ err = "PESession: random "; } else { char s[1025]; int l; if(Tcl_GetIntFromObj(interp,objv[2],&l) != TCL_ERROR){ if(l <= 1024){ Tcl_SetResult(interp, peRandomString(s,l,PRS_MIXED_CASE), TCL_STATIC); return(TCL_OK); } else err = "PESession: random length too long"; } else err = "PESession: can't get random length"; } } else if(!strcmp(op, "authdriver")){ if(objc != 4){ err = "PESession: authdriver {add | remove} drivername"; } else { char *cmd, *driver; if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if((driver = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ if(!strcmp(cmd,"enable")){ err = "PESession: authdriver enable disabled for the nonce"; } else if(!strcmp(cmd,"disable")){ if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_OK); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } } else err = "PESession: unknown authdriver operation"; } else err = "PESession: Can't read driver name"; } else err = "PESesions: Can't read authdriver operation"; } } else if(!strcmp(op, "abandon")){ /* * CMD: abandon [timeout] * * Returns: nothing */ if(objc != 3){ err = "PESession: abandon [timeout]"; } else { long t; if(Tcl_GetLongFromObj(interp, objv[2], &t) == TCL_OK){ /* ten second minimum and max of default */ if(t > 0 && t <= PE_INPUT_TIMEOUT){ gPEAbandonTimeout = t; return(TCL_OK); } else err = "unrecognized timeout"; } else err = "Can't read timeout"; } } else if(!strcmp(op, "noexpunge")){ /* * CMD: noexpunge * * Returns: nothing */ if(objc != 3){ err = "PESession: noexpunge "; } else { int onoff; if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){ if(onoff == 0 || onoff == 1){ ps_global->noexpunge_on_close = onoff; return(TCL_OK); } err = "unrecognized on/off state"; } else err = "Can't read on/off state"; } } else if(!strcmp(op, "setpassphrase")){ #ifdef SMIME char *passphrase; if(objc != 3){ err = "PESession: setpassphrase "; } else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){ if(ps_global && ps_global->smime){ strncpy((char *) ps_global->smime->passphrase, passphrase, sizeof(ps_global->smime->passphrase)); ps_global->smime->passphrase[sizeof(ps_global->smime->passphrase)-1] = '\0'; ps_global->smime->entered_passphrase = 1; ps_global->smime->need_passphrase = 0; peED.uid = 0; return(TCL_OK); } } #else err = "S/MIME not configured for this server"; #endif /* SMIME */ } else if(!strcmp(op, "expungecheck")) { /* * Return open folders and how many deleted messages they have * * return looks something like a list of these: * {folder-name number-deleted isinbox isincoming} */ char *type; long delete_count; Tcl_Obj *resObj; if(objc != 3){ err = "PESession: expungecheck "; } else { type = Tcl_GetStringFromObj(objv[2], NULL); if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){ if(ps_global->mail_stream != sp_inbox_stream() || strcmp(type, "current") == 0){ delete_count = count_flagged(ps_global->mail_stream, F_DEL); resObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(pretty_fn(ps_global->cur_folder), -1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj(delete_count)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj((ps_global->mail_stream == sp_inbox_stream()) ? 1 : 0)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj((ps_global->context_current->use & CNTXT_INCMNG) ? 1 : 0)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); } if(strcmp(type, "quit") == 0){ delete_count = count_flagged(sp_inbox_stream(), F_DEL); resObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj("INBOX", -1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj(delete_count)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj(1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewIntObj(1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); } return(TCL_OK); } else err = "PESession: expungecheck unknown type"; } } else if(!strcmp(op, "mailcheck")) { /* * CMD: mailcheck * * ARGS: reload -- "1" if we're reloading * (vs. just checking newmail as a side effect * of building a new page) * * Return list of folders with new or expunged messages * * return looks something like a list of these: * {new-count newest-uid announcement-msg} */ int reload, force = UFU_NONE, rv; time_t now = time(0); if(objc <= 3){ if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR) reload = 0; /* minimum 10 second between IMAP pings */ if(!time_of_last_input() || now - time_of_last_input() > 10){ force = UFU_FORCE; if(!reload) peMarkInputTime(); } peED.interp = interp; /* check for new mail */ new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG); if(!reload){ /* announced */ zero_new_mail_count(); } return(TCL_OK); } else err = "PESession: mailcheck "; } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * PEFolderChange - create context's directory chain * corresponding to list of given obj's * * NOTE: caller should call reset_context_folders(cp) to * clean up data structures this creates before returning */ int PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[]) { int i; FDIR_S *fp; char *folder; for(i = 0; i < objc; i++) { folder = Tcl_GetStringFromObj(objv[i], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolderChange: Can't read folder", TCL_VOLATILE); reset_context_folders(cp); return(TCL_ERROR); } fp = next_folder_dir(cp, folder, 0, NULL); /* BUG: mail_stream? */ fp->desc = folder_lister_desc(cp, fp); fp->delim = cp->dir->delim; fp->prev = cp->dir; fp->status |= CNTXT_SUBDIR; cp->dir = fp; } return(TCL_OK); } /* * PEMakeFolderString: */ int PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath) { int i; unsigned long size,len; char *portion,*path; size = 0; for(i = 0; i < objc; i++) { portion = Tcl_GetStringFromObj(objv[i], NULL); if(!portion) { Tcl_SetResult(interp, "PEMakeFolderString: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(i) size++; size += strlen(portion); } path = (char*) fs_get(size + 1); size = 0; for(i = 0; i < objc; i++) { portion = Tcl_GetStringFromObj(objv[i], NULL); len = strlen(portion); if(i) path[size++] = cp->dir->delim; memcpy(path + size, portion, len); size += len; } path[size] = '\0'; if(ppath) *ppath = path; else fs_give((void**) &path); return(TCL_OK); } /* * PEFolderCmd - export various bits of folder information */ int PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *op, errbuf[256], *err = "Unknown PEFolder request"; dprint((2, "PEFolderCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ if(ps_global){ if(objc == 2){ if(!strcmp(op, "current")){ CONTEXT_S *cp; int i; /* * CMD: current * * Returns: string representing the name of the * current mailbox */ for(i = 0, cp = ps_global->context_list; cp && cp != ps_global->context_current; i++, cp = cp->next) ; if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->cur_folder,-1)) == TCL_OK) return(TCL_OK); return(TCL_ERROR); } else if(!strcmp(op, "collections")){ CONTEXT_S *cp; int i; /* * CMD: collections * * Returns: List of currently configured collections */ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next){ Tcl_Obj *objv[3]; objv[0] = Tcl_NewIntObj(i); objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1); objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(3, objv)); } return(TCL_OK); } else if(!strcmp(op, "defaultcollection")){ int i; CONTEXT_S *cp; for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(cp->use & CNTXT_SAVEDFLT){ Tcl_SetResult(interp, int2string(i), TCL_STATIC); return(TCL_OK); } err = "PEFolder: isincoming: Invalid collection ID"; } else if(!strcmp(op, "clextended")){ CONTEXT_S *cp; int i; char tpath[MAILTMPLEN], *p; /* * CMD: clextended * * Returns: Extended list of current collections * * Format: * 0) Collection Number * 1) Nickname * 2) Label * 3) Basically this is a flag to say if we can edit * 4) Server * 5) Path * 6) View */ /* * had to get rid of this cause the args are changed * * if(strcmp("extended", * Tcl_GetStringFromObj(objv[2], NULL))){ * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE); * return(TCL_ERROR); * } */ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next){ Tcl_Obj *objv[7]; objv[0] = Tcl_NewIntObj(i); objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1); objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1); objv[3] = Tcl_NewIntObj(cp->var.v ? 1 : 0); objv[4] = Tcl_NewStringObj(cp->server ? cp->server : "", -1); tpath[0] = '\0'; if(cp->context){ strncpy(tpath, (cp->context[0] == '{' && (p = strchr(cp->context, '}'))) ? ++p : cp->context, sizeof(tpath)); tpath[sizeof(tpath)-1] = '\0'; if((p = strstr(tpath, "%s")) != NULL) *p = '\0'; } objv[5] = Tcl_NewStringObj(tpath, -1); objv[6] = Tcl_NewStringObj(cp->dir && cp->dir->view.user ? cp->dir->view.user : "", -1); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(7, objv)); } return(TCL_OK); } } else if(objc == 3 && !strcmp(op, "delimiter")){ int colid, i; char delim[2] = {'\0', '\0'}; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ if(cp->dir && cp->dir->delim) delim[0] = cp->dir->delim; break; } Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE); return(TCL_OK); } else err = "PEFolder: delimiter: Can't read collection ID"; } else if(objc == 3 && !strcmp(op, "isincoming")){ int colid, i; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ Tcl_SetResult(interp, int2string(((cp->use & CNTXT_INCMNG) != 0)), TCL_STATIC); return(TCL_OK); } err = "PEFolder: isincoming: Invalid collection ID"; } else err = "PEFolder: isincoming: Can't read collection ID"; } else if(objc == 4 && !strcmp(op, "unread")){ char *folder, tmp[MAILTMPLEN]; MAILSTREAM *mstream; CONTEXT_S *cp; long colid, i, count = 0, flags = (F_UNSEEN | F_UNDEL); int our_stream = 0; /* * CMD: unread * * Returns: number of unread messages in given * folder */ if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; if(cp){ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ /* short circuit INBOX */ if(colid == 0 && !strucmp(folder, "inbox")){ count = count_flagged(sp_inbox_stream(), flags); } else{ /* * BUG: some sort of caching to prevent open() fore each call? * does stream cache offset this? */ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp))) && (mstream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){ long retflags = 0; ps_global->noshow_error = 1; our_stream = 1; mstream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE| OP_READONLY | OP_SHORTCACHE, &retflags); ps_global->noshow_error = 0; } count = count_flagged(mstream, flags); if(our_stream) pine_mail_close(mstream); } Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } } else err = "PEFolder: unread: Invalid collection ID"; } else err = "PEFolder: unread: Can't read collection ID"; } else if(objc == 5 && !strcmp(op, "empty")){ /* * CMD: empty * * Returns: number of expunge messages * * Arguments: * where is either , 'selected', or 'all' */ CONTEXT_S *cp; MAILSTREAM *stream = NULL; MESSAGECACHE *mc; MSGNO_S *msgmap; int colid, i, our_stream = 0; long uid, raw, count = 0L; char *errstr = NULL, *what, *folder, *p, tmp[MAILTMPLEN]; if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; } if(cp){ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ if((what = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){ /* need to open? */ if(!((context_allowed(context_apply(tmp, cp, folder, sizeof(tmp))) && (stream = same_stream_and_mailbox(tmp, ps_global->mail_stream))) || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){ long retflags = 0; our_stream = 1; stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags); } if(stream){ msgmap = sp_msgmap(stream); if(!strucmp(what, "all")){ if(mn_get_total(msgmap)){ agg_select_all(stream, msgmap, NULL, 1); errstr = peApplyFlag(stream, msgmap, 'd', 0, &count); if(!errstr) (void) cmd_expunge_work(stream, msgmap, NULL); } } else{ /* little complicated since we don't display deleted state and * don't want to expunge what's not intended. * remember what's deleted and restore state on the ones left * when we're done. shouldn't happen much. * NOTE: "uid" is NOT a UID in this loop */ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){ raw = mn_m2raw(msgmap, uid); if(!get_lflag(stream, msgmap, uid, MN_EXLD) && (mc = mail_elt(stream, raw)) != NULL && mc->deleted){ set_lflag(stream, msgmap, uid, MN_STMP, 1); mail_flag(stream, long2string(raw), "\\DELETED", 0L); } else set_lflag(stream, msgmap, uid, MN_STMP, 0); } if(!strucmp(what,"selected")){ if(any_lflagged(msgmap, MN_SLCT)){ if(!(errstr = peApplyFlag(stream, msgmap, 'd', 0, &count))) (void) cmd_expunge_work(stream, msgmap, NULL); } else count = 0L; } else{ uid = 0; for(p = what; *p; p++) if(isdigit((unsigned char) *p)){ uid = (uid * 10) + (*p - '0'); } else{ errstr = "Invalid uid value"; break; } if(!errstr && uid){ /* uid is a UID here */ mail_flag(stream, long2string(uid), "\\DELETED", ST_SET | ST_UID); (void) cmd_expunge_work(stream, msgmap, NULL); count = 1L; } } /* restore deleted on what didn't get expunged */ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){ raw = mn_m2raw(msgmap, uid); if(get_lflag(stream, msgmap, uid, MN_STMP)){ set_lflag(stream, msgmap, uid, MN_STMP, 0); mail_flag(stream, long2string(raw), "\\DELETED", ST_SET); } } } if(our_stream) pine_mail_close(stream); } else errstr = "no stream"; } else errstr = "Cannot get which "; } else errstr = "Cannot get folder"; } else errstr = "Invalid collection"; if(errstr){ Tcl_SetResult(interp, errstr, TCL_VOLATILE); return(TCL_ERROR); } Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "export")){ /* * CMD: export * * Returns: success or failure after writing given * folder to given local file. * * Format: * 0) Collection Number * 1) Folder * 2) Destination file */ if(objc == 5){ CONTEXT_S *cp; MAILSTREAM *src; APPEND_PKG pkg; STRING msg; long colid, i; char *folder, *dfile, seq[64], tmp[MAILTMPLEN]; int our_stream = 0; if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; if(cp){ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ if((dfile = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){ snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile); if(pine_mail_create(NULL, tmp)){ err = NULL; /* reset error condition */ /* * if not current folder, open a stream, setup the * stuff to write the raw header/text by hand * with berkeley delimiters since we don't want * a local mailbox driver lunk in. * * comments: * - BUG: what about logins? * */ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp))) && (src = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){ long retflags = 0; our_stream = 1; src = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE, &retflags); } if(src && src->nmsgs){ /* Go to work...*/ pkg.stream = src; pkg.msgno = 0; pkg.msgmax = src->nmsgs; pkg.flags = pkg.date = NIL; pkg.message = &msg; snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs); mail_fetchfast (src, seq); ps_global->noshow_error = 1; if(!mail_append_multiple (NULL, dfile, peAppendMsg, (void *) &pkg)){ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: export: %.200s", ps_global->c_client_error); } ps_global->noshow_error = 0; if(our_stream) pine_mail_close(src); } else err = "PEFolder: export: can't open mail folder"; if(!err) return(TCL_OK); } else err = "PEFolder: export: can't create destination"; if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")) err = "PEFolder: export: can't disable driver"; } else err = "PEFolder: export: can't enable driver"; } else err = "PEFolder: export: can't read file name"; } else err = "PEFolder: export: can't read folder name"; } else err = "PEFolder: export: Invalid collection ID"; } else err = "PEFolder:export: Can't read collection ID"; } else err = "PEFolder: export "; } else if(!strcmp(op, "import")){ /* * CMD: import * * Returns: success or failure after writing given * folder to given local file. * * Format: * 0) source file * 1) destination collection number * 2) destination folder */ if(objc == 5){ CONTEXT_S *cp; MAILSTREAM *src, *dst; APPEND_PKG pkg; STRING msg; long colid, i; char *folder, *sfile, seq[64]; /* get source file with a little sanity check */ if((sfile = Tcl_GetStringFromObj(objv[2], NULL)) && *sfile == '/' && !strstr(sfile, "..")){ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){ ps_global->noshow_error = 1; /* don't queue error msg */ err = NULL; /* reset error condition */ /* make sure sfile contains valid mail */ if((src = mail_open(NULL, sfile, 0L)) != NULL){ if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; if(cp){ if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){ long retflags = 0; if(context_create(cp, NULL, folder) && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){ if(src->nmsgs){ /* Go to work...*/ pkg.stream = src; pkg.msgno = 0; pkg.msgmax = src->nmsgs; pkg.flags = pkg.date = NIL; pkg.message = &msg; snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs); mail_fetchfast (src, seq); if(!context_append_multiple(cp, dst, folder, peAppendMsg, (void *) &pkg, ps_global->mail_stream)){ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s", ps_global->c_client_error); } } pine_mail_close(dst); } else snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s", ps_global->c_client_error); } else err = "PEFolder: import: can't read folder name"; } else err = "PEFolder:import: invalid collection id"; } else err = "PEFolder: import: can't read collection id"; mail_close(src); } else snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s", ps_global->c_client_error); ps_global->noshow_error = 0; if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err) err = "PEFolder: import: can't disable driver"; if(!err) return(TCL_OK); } else err = "PEFolder: import: can't enable driver"; } else err = "PEFolder: import: can't read file name"; } else err = "PEFolder: import "; } else { int i, colid; char *aes, *colstr; CONTEXT_S *cp; /* * 3 or more arguments, 3rd is the collection ID, rest * are a folder name */ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; } else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(!strcmp("default", colstr)) cp = default_save_context(ps_global->context_list); else cp = NULL; } else cp = NULL; if(cp){ if(!strcmp(op, "list")){ int i, fcount, bflags = BFL_NONE; if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR) return TCL_ERROR; if(cp->use & CNTXT_NEWS) bflags |= BFL_LSUB; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); build_folder_list(NULL, cp, "*", NULL, bflags); if((aes = peAuthException()) != NULL){ Tcl_SetResult(interp, aes, TCL_VOLATILE); reset_context_folders(cp); return(TCL_ERROR); } if((fcount = folder_total(FOLDERS(cp))) != 0){ for(i = 0; i < fcount; i++){ char type[3], *p; FOLDER_S *f = folder_entry(i, FOLDERS(cp)); p = type; if(f->isdir){ *p++ = 'D'; if(f->hasnochildren && !f->haschildren) *p++ = 'E'; } if(f->isfolder || f->nickname || (cp->use & CNTXT_INCMNG)) *p++ = 'F'; *p = '\0'; peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type, f->nickname ? f->nickname : f->name); } } reset_context_folders(cp); return(TCL_OK); } else if(!strucmp(op, "exists")){ char *folder, *errstr = NULL; int rv; if(objc < 4) { Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE); return(TCL_ERROR); } folder = Tcl_GetStringFromObj(objv[objc - 1], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolder exists: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR) return TCL_ERROR; ps_global->c_client_error[0] = '\0'; pePrepareForAuthException(); rv = folder_name_exists(cp, folder, NULL); if(rv & FEX_ERROR){ if((errstr = peAuthException()) == NULL){ if(ps_global->c_client_error[0]) errstr = ps_global->c_client_error; else errstr = "Indeterminate Error"; } } Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE); return(errstr ? TCL_ERROR : TCL_OK); } else if(!strucmp(op, "fullname")){ char *folder, *fullname; if(objc < 4) { Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE); return(TCL_ERROR); } folder = Tcl_GetStringFromObj(objv[objc - 1], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolder fullname: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR) return TCL_ERROR; #if 0 Tcl_Obj *obj = Tcl_NewStringObj((fullname = folder_is_nick(folder, FOLDERS(cp))) ? fullname : folder, -1); (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), obj); #else Tcl_SetResult(interp, (fullname = folder_is_nick(folder, FOLDERS(cp), FN_NONE)) ? fullname : folder, TCL_VOLATILE); #endif return(TCL_OK); } else if(!strucmp(op, "create")){ char *aes, *folder; folder = Tcl_GetStringFromObj(objv[objc - 1], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolder create: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR) return TCL_ERROR; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); if(!context_create(cp, NULL, folder)){ if((aes = peAuthException()) != NULL){ Tcl_SetResult(interp, aes, TCL_VOLATILE); } else{ Tcl_SetResult(interp, (ps_global->last_error[0]) ? ps_global->last_error : (ps_global->c_client_error[0]) ? ps_global->c_client_error : "Unable to create folder", TCL_VOLATILE); } reset_context_folders(cp); return(TCL_ERROR); } Tcl_SetResult(interp, "OK", TCL_STATIC); reset_context_folders(cp); return(TCL_OK); } else if(!strucmp(op, "delete")){ int fi, readonly, close_opened = 0; char *folder, *fnamep, *target = NULL, *aes; MAILSTREAM *del_stream = NULL, *strm = NULL; EditWhich ew; PINERC_S *prc = NULL; FOLDER_S *fp; folder = Tcl_GetStringFromObj(objv[objc - 1], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolder delete: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR) return TCL_ERROR; /* so we can check for folder's various properties */ build_folder_list(NULL, cp, folder, NULL, BFL_NONE); ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); /* close open folder, then delete */ if((fi = folder_index(folder, cp, FI_FOLDER)) < 0 || (fp = folder_entry(fi, FOLDERS(cp))) == NULL){ Tcl_SetResult(interp, "Cannot find folder to delete", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } if(!((cp->use & CNTXT_INCMNG) && fp->name && check_for_move_mbox(fp->name, NULL, 0, &target))){ target = NULL; } dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?")); ew = config_containing_inc_fldr(fp); if(ps_global->restricted) readonly = 1; else{ switch(ew){ case Main: prc = ps_global->prc; break; case Post: prc = ps_global->post_prc; break; case None: break; } readonly = prc ? prc->readonly : 1; } if(prc && prc->quit_to_edit && (cp->use & CNTXT_INCMNG)){ Tcl_SetResult(interp, "Must Exit Alpine to Change Configuration", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } if(cp == ps_global->context_list && !(cp->dir && cp->dir->ref) && strucmp(folder, ps_global->inbox_name) == 0){ Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } else if(readonly && (cp->use & CNTXT_INCMNG)){ Tcl_SetResult(interp, "Folder not in editable config file", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } else if((fp->name && (strm=context_already_open_stream(cp,fp->name,AOS_NONE))) || (target && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){ if(strm == ps_global->mail_stream) close_opened++; } else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */ FDIR_S *fdirp = next_folder_dir(cp,folder,TRUE,NULL); int ret; if(fp->haschildren) ret = 1; else if(fp->hasnochildren) ret = 0; else{ ret = folder_total(fdirp->folders) > 0; free_fdir(&fdirp, 1); } if(ret){ Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } /* * Folder by the same name exist, so delete both... if(fp->isdual){ Tcl_SetResult(interp, "Cannot delete: folder is also a directory", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } */ } if(cp->use & CNTXT_INCMNG){ Tcl_SetResult(interp, "Cannot delete incoming folder", TCL_STATIC); reset_context_folders(cp); return(TCL_ERROR); } dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n", fp->name ? fp->name : "?", fp->nickname ? fp->nickname : "", cp->context ? cp->context : "?")); if(strm){ /* * Close it, NULL the pointer, and let do_broach_folder fixup * the rest... */ pine_mail_actually_close(strm); if(close_opened){ do_broach_folder(ps_global->inbox_name, ps_global->context_list, NULL, DB_INBOXWOCNTXT); } } /* * Use fp->name since "folder" may be a nickname... */ if(ps_global->mail_stream && context_same_stream(cp, fp->name, ps_global->mail_stream)) del_stream = ps_global->mail_stream; fnamep = fp->name; if(!context_delete(cp, del_stream, fnamep)){ if((aes = peAuthException()) != NULL){ Tcl_SetResult(interp, aes, TCL_VOLATILE); } else{ Tcl_SetResult(interp, (ps_global->last_error[0]) ? ps_global->last_error : (ps_global->c_client_error[0]) ? ps_global->c_client_error : "Unable to delete folder", TCL_VOLATILE); } reset_context_folders(cp); return(TCL_ERROR); } Tcl_SetResult(interp, "OK", TCL_STATIC); reset_context_folders(cp); return(TCL_OK); } /* * must be at least 5 arguments for the next set of commands */ else if(objc < 5) { Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE); return(TCL_ERROR); } else if(!strucmp(op, "rename")){ char *folder,*newfolder, *aes; folder = Tcl_GetStringFromObj(objv[objc - 2], NULL); if(!folder) { Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } newfolder = Tcl_GetStringFromObj(objv[objc - 1], NULL); if(!newfolder) { Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE); return(TCL_ERROR); } if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR) return TCL_ERROR; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); if(!context_rename(cp, NULL, folder, newfolder)){ if((aes = peAuthException()) != NULL){ Tcl_SetResult(interp, aes, TCL_VOLATILE); } else{ Tcl_SetResult(interp, (ps_global->last_error[0]) ? ps_global->last_error : (ps_global->c_client_error[0]) ? ps_global->c_client_error : "Unable to rename folder", TCL_VOLATILE); } reset_context_folders(cp); return(TCL_ERROR); } Tcl_SetResult(interp, "OK", TCL_STATIC); reset_context_folders(cp); return(TCL_OK); } } else err = "PEFolder: Unrecognized collection ID"; } } else err = "No User Context Established"; } Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } /* * PEMailboxCmd - export various bits of mailbox information */ int PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *op, errbuf[256], *err = "Unknown PEMailbox operation"; dprint((5, "PEMailboxCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ if(!strucmp(op, "open")){ int i, colid; char *folder; CONTEXT_S *cp; peED.uid = 0; /* forget cached embedded data */ /* * CMD: open * * */ if(objc == 2){ Tcl_SetResult(interp, (!sp_dead_stream(ps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE); return(TCL_OK); } if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){ if((folder = Tcl_GetStringFromObj(objv[objc - 1], NULL)) != NULL) { for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) { if(PEMakeFolderString(interp, cp, objc - 3, objv + 3, &folder)) return TCL_ERROR; dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder)); return(peCreateStream(interp, cp, folder, FALSE)); } err = "open: Unrecognized collection ID"; } else err = "open: Can't read folder"; } else err = "open: Can't get collection ID"; } else if(!strcmp(op, "indexformat")){ /* * CMD: indexformat * * Returns: list of lists where: * * the first element is the name of the * field which may be "From", "Subject" * "Date" or the empty string. * * the second element which is either * the percentage width or empty string */ if(objc == 2) return(peIndexFormat(interp)); } else if(ps_global && ps_global->mail_stream){ if(!strcmp(op, "select")){ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT)); } else if(!strcmp(op, "search")){ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH)); } else if(!strucmp(op, "apply")){ return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2])); } else if(!strcmp(op, "expunge")){ /* * CMD: expunge * * Returns: OK after having removed deleted messages */ char *streamstr = NULL; MAILSTREAM *stream; MSGNO_S *msgmap; if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL); if(!streamstr || (streamstr && (strcmp(streamstr, "current") == 0))){ stream = ps_global->mail_stream; msgmap = sp_msgmap(stream); } else if(streamstr && (strcmp(streamstr, "inbox") == 0)){ stream = sp_inbox_stream(); msgmap = sp_msgmap(stream); } else return(TCL_ERROR); ps_global->last_error[0] = '\0'; if(IS_NEWS(stream) && stream->rdonly){ msgno_exclude_deleted(stream, msgmap, NULL); clear_index_cache(sp_inbox_stream(), 0); /* * This is kind of surprising at first. For most sort * orders, if the whole set is sorted, then any subset * is also sorted. Not so for OrderedSubject sort. * If you exclude the first message of a subject group * then you change the date that group is to be sorted on. */ if(mn_get_sort(msgmap) == SortSubject2) refresh_sort(ps_global->mail_stream, msgmap, FALSE); } else (void) cmd_expunge_work(stream, msgmap, NULL); Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "trashdeleted")){ /* * CMD: trashdeleted * * Returns: OK after moving deleted messages to Trash and expunging */ MAILSTREAM *stream; MESSAGECACHE *mc; CONTEXT_S *cp; MSGNO_S *msgmap; char *streamstr = NULL, tmp[MAILTMPLEN]; long n, tomove = 0L; if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL); if(!streamstr || (streamstr && (strcmp(streamstr, "current") == 0))){ stream = ps_global->mail_stream; msgmap = sp_msgmap(stream); } else if(streamstr && (strcmp(streamstr, "inbox") == 0)){ stream = sp_inbox_stream(); msgmap = sp_msgmap(stream); } else return(TCL_ERROR); ps_global->last_error[0] = '\0'; if(IS_NEWS(stream) && stream->rdonly){ msgno_exclude_deleted(stream, msgmap, NULL); clear_index_cache(sp_inbox_stream(), 0); /* * This is kind of surprising at first. For most sort * orders, if the whole set is sorted, then any subset * is also sorted. Not so for OrderedSubject sort. * If you exclude the first message of a subject group * then you change the date that group is to be sorted on. */ if(mn_get_sort(msgmap) == SortSubject2) refresh_sort(ps_global->mail_stream, msgmap, FALSE); } else{ if(!(cp = default_save_context(ps_global->context_list))) cp = ps_global->context_list; /* copy to trash if we're not in trash */ if(ps_global->VAR_TRASH_FOLDER && ps_global->VAR_TRASH_FOLDER[0] && context_allowed(context_apply(tmp, cp, ps_global->VAR_TRASH_FOLDER, sizeof(tmp))) && !same_stream_and_mailbox(tmp, stream)){ /* save real selected set, and */ for(n = 1L; n <= mn_get_total(msgmap); n++){ set_lflag(stream, msgmap, n, MN_STMP, get_lflag(stream, msgmap, n, MN_SLCT)); /* select deleted */ if(!get_lflag(stream, msgmap, n, MN_EXLD) && (mc = mail_elt(stream, mn_m2raw(msgmap,n))) != NULL && mc->deleted){ tomove++; set_lflag(stream, msgmap, n, MN_SLCT, 1); } else set_lflag(stream, msgmap, n, MN_SLCT, 0); } if(tomove && pseudo_selected(stream, msgmap)){ /* save deleted to Trash */ n = save(ps_global, stream, cp, ps_global->VAR_TRASH_FOLDER, msgmap, SV_FOR_FILT | SV_FIX_DELS); /* then remove them */ if(n == tomove){ (void) cmd_expunge_work(stream, msgmap, NULL); } restore_selected(msgmap); } /* restore selected set */ for(n = 1L; n <= mn_get_total(msgmap); n++) set_lflag(stream, msgmap, n, MN_SLCT, get_lflag(stream, msgmap, n, MN_STMP)); } } Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "nextvector")){ long msgno, count, countdown; int i, aObjN = 0; char *errstr = NULL, *s; Tcl_Obj *rvObj, *vObj, *avObj, **aObj; /* * CMD: nextvector * * ARGS: msgno - message number "next" is relative to * count - how many msgno slots to return * attrib - (optional) attributes to be returned with each message in vector * * Returns: vector containing next messagenumbers (and optional attributes) */ if(objc == 4 || objc == 5){ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) == TCL_OK){ if(Tcl_GetLongFromObj(interp, objv[3], &count) == TCL_OK){ /* set index range for efficiency */ if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream))){ gPeITop = msgno; gPeICount = count; } if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){ if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno); for(countdown = count; countdown > 0; countdown--){ imapuid_t uid = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno)); int fetched = 0; if((vObj = Tcl_NewListObj(0, NULL)) != NULL){ Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno)); peAppListF(interp, vObj, "%lu", uid); if(aObjN){ if((avObj = Tcl_NewListObj(0, NULL)) != NULL){ for(i = 0; i < aObjN; i++){ if((s = Tcl_GetStringFromObj(aObj[i], NULL)) != NULL){ if(!strcmp(s, "statusbits")){ char *s = peMsgStatBitString(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), gPeITop, gPeICount, &fetched); Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1)); } else if(!strcmp(s, "statuslist")){ Tcl_Obj *nObj = peMsgStatNameList(interp, ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), gPeITop, gPeICount, &fetched); Tcl_ListObjAppendElement(interp, avObj, nObj); } else if(!strcmp(s, "status")){ long raw; char stat[3]; MESSAGECACHE *mc; raw = peSequenceNumber(uid); if(!((mc = mail_elt(ps_global->mail_stream, raw)) && mc->valid)){ mail_fetch_flags(ps_global->mail_stream, ulong2string(uid), FT_UID); mc = mail_elt(ps_global->mail_stream, raw); } stat[0] = mc->deleted ? '1' : '0'; stat[1] = mc->recent ? '1' : '0'; stat[2] = mc->seen ? '1' : '0'; Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3)); } else if(!strcmp(s, "indexparts")){ Tcl_Obj *iObj; if((iObj = Tcl_NewListObj(0, NULL)) != NULL && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){ } else return(TCL_ERROR); } else if(!strucmp(s, "indexcolor")){ if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK) return(TCL_ERROR); } } else{ errstr = "nextvector: can't read attributes"; break; } } Tcl_ListObjAppendElement(interp, vObj, avObj); } else{ errstr = "nextvector: can't allocate attribute return vector"; break; } } } else{ errstr = "nextvector: can't allocate new vector"; break; } Tcl_ListObjAppendElement(interp, rvObj, vObj); for(++msgno; msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream)) && msgline_hidden(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_NONE); msgno++) ; if(msgno > mn_get_total(sp_msgmap(ps_global->mail_stream))) break; } } if(!errstr){ /* append result vector */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), rvObj); /* Everything is coerced to UTF-8 */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("UTF-8", -1)); return(TCL_OK); } } else errstr = "nextvector: can't read attribute list"; } else errstr = "nextvector: can't read count"; } else errstr = "nextvector: can't read message number"; } else errstr = "nextvector: Incorrect number of arguments"; if(errstr) Tcl_SetResult(interp, errstr, TCL_STATIC); return(TCL_ERROR); } else if(objc == 2){ if(!strcmp(op, "messagecount")){ /* * CMD: messagecount * * Returns: count of messages in open mailbox */ Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "firstinteresting")){ /* * CMD: firstinteresting * * Returns: message number associated with * "incoming-startup-rule" which had better * be the "current" message since it was set * in do_broach_folder and shouldn't have been * changed otherwise (expunged screw us?) */ Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "selected")){ /* * CMD: selected * * Returns: count of selected messages in open mailbox */ Tcl_SetResult(interp, long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "searched")){ /* * CMD: searched * * Returns: count of searched messages in open mailbox */ Tcl_SetResult(interp, long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH)), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "mailboxname")){ /* * CMD: name * * Returns: string representing the name of the * current mailbox */ Tcl_SetResult(interp, ps_global->cur_folder, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "close")){ /* * CMD: close * * Returns: with global mail_stream closed */ peDestroyStream(ps_global); return(TCL_OK); } else if(!strcmp(op, "newmailreset")){ sml_seen(); zero_new_mail_count(); sp_set_mail_box_changed(ps_global->mail_stream, 0); sp_set_expunge_count(ps_global->mail_stream, 0L); peMarkInputTime(); return(TCL_OK); } else if(!strcmp(op, "newmailstatmsg")){ long newest, count; char subject[500], subjtxt[500], from[500], intro[500], *s = ""; /* * CMD: newmailstatmsg * * ARGS: none * * Returns: text for new mail message * */ if(sp_mail_box_changed(ps_global->mail_stream) && (count = sp_mail_since_cmd(ps_global->mail_stream))){ for(newest = ps_global->mail_stream->nmsgs; newest > 1L; newest--) if(!get_lflag(ps_global->mail_stream, NULL, newest, MN_EXLD)) break; if(newest){ format_new_mail_msg(NULL, count, pine_mail_fetchstructure(ps_global->mail_stream, newest, NULL), intro, from, subject, subjtxt, sizeof(subject)); snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt); } } Tcl_SetResult(interp, s, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "savedefault")){ return(peSaveDefault(interp, 0L, 0, NULL)); } else if(!strcmp(op, "gotodefault")){ return(peGotoDefault(interp, 0L, NULL)); } else if(!strcmp(op, "zoom")){ Tcl_SetResult(interp, long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L) ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT) : 0L), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "focus")){ Tcl_SetResult(interp, long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L) ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH) : 0L), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "first")){ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){ long n; for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++) if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE); return(TCL_OK); } unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream)); } Tcl_SetResult(interp, int2string(1), TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(op, "current")){ long n = 0; unsigned long u = 0; /* * CMD: current * * ARGS: * * Returns: list of current msg { } */ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 0 || ((n = mn_get_cur(sp_msgmap(ps_global->mail_stream))) > 0 && (u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) > 0)){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(u), -1)); return(TCL_OK); } else err = "Cannot get current"; } else if(!strcmp(op, "last")){ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){ long n; for(n = mn_get_total(sp_msgmap(ps_global->mail_stream)); n > 0L; n--) if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE); return(TCL_OK); } } else{ Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE); return(TCL_OK); } Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC); return(TCL_ERROR); } else if(!strucmp(op, "sortstyles")){ int i; /* * CMD: sortstyles * * Returns: list of supported sort styles */ for(i = 0; ps_global->sort_types[i] != EndofList; i++) if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(sort_name(ps_global->sort_types[i]), -1)) != TCL_OK) return(TCL_ERROR); return(TCL_OK); } else if(!strucmp(op, "sort")){ return(peAppendCurrentSort(interp)); } else if(!strucmp(op, "state")){ if(!ps_global->mail_stream || sp_dead_stream(ps_global->mail_stream)) Tcl_SetResult(interp, "closed", TCL_STATIC); else if(ps_global->mail_stream->rdonly && !IS_NEWS(ps_global->mail_stream)) Tcl_SetResult(interp, "readonly", TCL_STATIC); else Tcl_SetResult(interp, "ok", TCL_STATIC); return(TCL_OK); } else if(!strucmp(op, "excludedeleted")){ msgno_exclude_deleted(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL); return(TCL_OK); } } else if(objc == 3){ if(!strcmp(op, "uid")){ long msgno, raw; /* * Return uid of given message number * * CMD: uid */ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK) return(TCL_ERROR); /* conversion problem? */ if((raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno)) > 0L){ raw = mail_uid(ps_global->mail_stream, raw); Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE); return(TCL_OK); } snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } else if(!strcmp(op, "newmail")){ int reload, force = UFU_NONE, rv; time_t now = time(0); /* * CMD: newmail * * ARGS: reload -- "1" if we're reloading * (vs. just checking newmail as a side effect * of building a new page) * * Returns: count - * mostrecent - */ if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR) reload = 0; /* minimum 10 second between IMAP pings */ if(!time_of_last_input() || now - time_of_last_input() > 10){ force = UFU_FORCE; peMarkInputTime(); } /* check for new mail */ new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE); rv = peNewMailResult(interp); if(!reload) /* announced */ zero_new_mail_count(); return(rv); } else if(!strcmp(op, "flagcount")){ char *flag; long count = 0L; long flags = 0L; int objlc; Tcl_Obj **objlv; /* * CMD: flagcount * * ARGS: flags - * * Returns: count - number of message thusly flagged * mostrecent - */ if(Tcl_ListObjGetElements(interp, objv[2], &objlc, &objlv) == TCL_OK){ while(objlc--) if((flag = Tcl_GetStringFromObj(*objlv++, NULL)) != NULL){ if(!strucmp(flag, "deleted")){ flags |= F_DEL; } if(!strucmp(flag, "undeleted")){ flags |= F_UNDEL; } else if(!strucmp(flag, "seen")){ flags |= F_SEEN; } else if(!strucmp(flag, "unseen")){ flags |= F_UNSEEN; } else if(!strucmp(flag, "flagged")){ flags |= F_FLAG; } else if(!strucmp(flag, "unflagged")){ flags |= F_UNFLAG; } else if(!strucmp(flag, "answered")){ flags |= F_ANS; } else if(!strucmp(flag, "unanswered")){ flags |= F_UNANS; } else if(!strucmp(flag, "recent")){ flags |= F_RECENT; } else if(!strucmp(flag, "unrecent")){ flags |= F_UNRECENT; } } if(flags) count = count_flagged(ps_global->mail_stream, flags); } Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "zoom")){ int newstate; long n, zoomed = 0L; /* * CMD: zoom * * Set/clear HID bits of non SLCT messages as requested. * PEMailbox [first | last | next] are senstive to these flags. * * ARGS: newstate - 1 or 0 * * Returns: count of zoomed messages */ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){ if(newstate > 0){ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)))){ zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SLCT); zoomed = n; } } else{ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)) unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream)); } } Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "focus")){ int newstate; long n, zoomed = 0L; /* * CMD: focus * * Set/clear HID bits of non MN_SRCH messages as requested. * PEMailbox [first | last | next] are senstive to MN_HIDE flag * * ARGS: newstate - 1 or 0 * * Returns: count of zoomed messages */ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){ if(newstate > 0){ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH)))) zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SRCH); zoomed = n; } else{ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)) unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream)); } } Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(op, "next")){ long msgno; /* * CMD: next * * ARGS: msgno - message number "next" is relative to * * Returns: previous state */ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno); mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE); Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE); return(TCL_OK); } Tcl_SetResult(interp, "next can't read message number", TCL_STATIC); return(TCL_ERROR); } } else if(objc == 4){ if(!strucmp(op, "sort")){ int i, reversed = 0; char *sort; /* * CMD: sort sortstyle reversed * * Returns: OK with the side-effect of message * numbers now reflecting the requested * sort order. */ if((sort = Tcl_GetStringFromObj(objv[2], NULL)) && Tcl_GetIntFromObj(interp, objv[3], &reversed) != TCL_ERROR){ /* convert sort string into */ for(i = 0; ps_global->sort_types[i] != EndofList; i++) if(strucmp(sort_name(ps_global->sort_types[i]), sort) == 0){ if(sp_unsorted_newmail(ps_global->mail_stream) || !(ps_global->sort_types[i] == mn_get_sort(sp_msgmap(ps_global->mail_stream)) && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), ps_global->sort_types[i], reversed, 0); break; } } return(peAppendCurrentSort(interp)); } else if(!strucmp(op, "selected")){ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT)); } else if(!strucmp(op, "searched")){ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH)); } else if(!strcmp(op, "next")){ long msgno, count; /* * CMD: next * * ARGS: msgno - message number "next" is relative to * count - how many to increment it * * Returns: previous state */ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno); while(count) if(count > 0){ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE); count--; } else{ mn_dec_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE); count++; } Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE); return(TCL_OK); } Tcl_SetResult(interp, "next can't read message number", TCL_STATIC); return(TCL_ERROR); } else if(!strcmp(op, "x-nextvector")){ long msgno, count; /* * CMD: nextvector * * ARGS: msgno - message number "next" is relative to * count - how many msgno slots to return * * Returns: vector containing next messagenumbers */ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){ if(count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno); while(count--){ long n = mn_get_cur(sp_msgmap(ps_global->mail_stream)); if(peAppListF(interp, Tcl_GetObjResult(interp), "%l%l", n, mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) != TCL_OK) return(TCL_ERROR); mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE); if(n == mn_get_cur(sp_msgmap(ps_global->mail_stream))) break; } } return(TCL_OK); } Tcl_SetResult(interp, "next can't read message number", TCL_STATIC); return(TCL_ERROR); } else if(!strcmp(op, "messagecount")){ char *relative; long msgno, n, count = 0L; /* * CMD: messagecount * * ARGS: [before | after] relative to * msgno * * Returns: count of messsages before or after given message number */ if((relative = Tcl_GetStringFromObj(objv[2], NULL)) && Tcl_GetLongFromObj(interp, objv[3], &msgno) != TCL_ERROR){ if(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream))){ Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC); return(TCL_ERROR); } if(!strucmp(relative, "before")){ for(n = msgno - 1; n > 0L; n--) if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)) count++; Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(relative, "after")){ for(n = msgno + 1; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++) if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)) count++; Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } } Tcl_SetResult(interp, "can't read range for count", TCL_STATIC); return(TCL_ERROR); } else if(!strcmp(op, "selectvector")){ long msgno, count; /* * CMD: selectvector * * ARGS: msgno - message number "next" is relative to * count - how many msgno slots to return * * Returns: vector containing next messagenumbers */ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){ if(msgno > 0L){ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno); while(count--){ msgno = mn_get_cur(sp_msgmap(ps_global->mail_stream)); if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT)) if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj((long) mail_uid(ps_global->mail_stream, msgno))) != TCL_OK) return(TCL_ERROR); mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE); if(msgno == mn_get_cur(sp_msgmap(ps_global->mail_stream))) break; } } return(TCL_OK); } Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC); return(TCL_ERROR); } else if(!strucmp(op, "current")){ char *which; long x, n = 0, u = 0; /* * CMD: current * * ARGS: (number|uid) * * Returns: list of current msg { } */ if((which = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(Tcl_GetLongFromObj(interp, objv[3], &x) == TCL_OK){ if(!strucmp(which,"uid")){ u = x; n = peMessageNumber(u); } else if(!strucmp(which,"number")){ n = x; u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n)); } if(n && u){ mn_set_cur(sp_msgmap(ps_global->mail_stream), n); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(u), -1)); return(TCL_OK); } else err = "PEMailbox current: invalid number/uid"; } else err = "PEMailbox current: cannot get number"; } else err = "PEMailbox current: cannot get which"; } } else err = "PEMailbox: Too many arguments"; } else if(!strucmp(op, "name") || !strcmp(op, "close")){ Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } else snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox", Tcl_GetStringFromObj(objv[0], NULL), op); } Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } int peAppendCurrentSort(Tcl_Interp *interp) { return((Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(ps_global->mail_stream))), -1)) == TCL_OK && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(mn_get_revsort(sp_msgmap(ps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK) ? TCL_OK : TCL_ERROR); } int peAppendDefaultSort(Tcl_Interp *interp) { return((Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(sort_name(ps_global->def_sort), -1)) == TCL_OK && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK) ? TCL_OK : TCL_ERROR); } int peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { char *subcmd; long n, i, diff, msgno; int narrow, hidden; MESSAGECACHE *mc; extern MAILSTREAM *mm_search_stream; extern long mm_search_count; hidden = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L; mm_search_stream = ps_global->mail_stream; mm_search_count = 0L; for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++) if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){ mc->searched = 0; mc->spare7 = 1; } /* * CMD: select * * ARGS: subcmd subcmdargs * * Returns: flip "matchflag" private bit on all or none * of the messages in the mailbox */ if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){ if(!strucmp(subcmd, "all")){ /* * Args: */ if(matchflag & MN_SLCT){ if(objc != 1) return(peSelectError(interp, subcmd)); agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL, 1); } else if(matchflag & MN_SRCH){ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++) set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 1); } Tcl_SetResult(interp, "All", TCL_VOLATILE); } else if(!strucmp(subcmd, "none")){ /* * Args: */ n = 0L; if(matchflag & MN_SLCT){ if(objc != 1) return(peSelectError(interp, subcmd)); agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), &n, 0); } else if(matchflag & MN_SRCH){ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++) set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 0); } Tcl_SetResult(interp, long2string(n), TCL_VOLATILE); } else if(!strucmp(subcmd, "searched")){ /* * Args: */ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++) if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){ i++; set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 1); } Tcl_SetResult(interp, long2string(i), TCL_VOLATILE); } else if(!strucmp(subcmd, "unsearched")){ /* * Args: */ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++) if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){ i++; set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 0); } Tcl_SetResult(interp, long2string(i), TCL_VOLATILE); } else{ if(!strucmp(subcmd, "narrow")) narrow = 1; else if(!strucmp(subcmd, "broad")) narrow = 0; else return(peSelectError(interp, "invalid scope request")); if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL))) return(peSelectError(interp, "missing subcommand")); if(!strucmp(subcmd, "num")){ if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK) return(i); } else if(!strucmp(subcmd, "date")){ if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK) return(i); } else if(!strucmp(subcmd, "text")){ if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK) return(i); } else if(!strucmp(subcmd, "status")){ if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK) return(i); } else if(!strucmp(subcmd, "compound")){ char *s; int nSearchList, nSearch; Tcl_Obj **oSearchList, **oSearch; /* BUG: should set up one SEARCHPGM to fit criteria and issue single search */ if(Tcl_ListObjGetElements(interp, objv[2], &nSearchList, &oSearchList) == TCL_OK){ for(i = 0; i < nSearchList; i++){ if(Tcl_ListObjGetElements(interp, oSearchList[i], &nSearch, &oSearch) == TCL_OK){ if((s = Tcl_GetStringFromObj(oSearch[0], NULL)) != NULL){ if(!strucmp(s,"date")){ if((n = peSelectDate(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK) return(n); } else if(!strucmp(s,"text")){ if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK) return(n); } else if(!strucmp(s,"status")){ if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK) return(n); } else return(peSelectError(interp, "unknown compound search")); /* logical AND the results */ mm_search_count = 0L; for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++) if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){ if(mc->searched && mc->spare7) mm_search_count++; else mc->searched = mc->spare7 = 0; } } else return(peSelectError(interp, "malformed compound search")); } else return(peSelectError(interp, "malformed compound search")); } } else return(peSelectError(interp, "malformed compound search")); } else return(peSelectError(interp, "cmd cmdargs")); /* * at this point all interesting messages should * have searched bit lit */ if(narrow) /* make sure something was selected */ for(i = 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++) if(mail_elt(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)) break; else mm_search_count--; } diff = 0L; if(mm_search_count){ /* * loop thru all the messages, adjusting local flag bits * based on their "searched" bit... */ for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++) if(narrow){ /* turning OFF selectedness if the "searched" bit isn't lit. */ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){ if(!mail_elt(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){ diff--; set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 0); if(hidden) set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 1); } else if(msgno < mn_get_cur(sp_msgmap(ps_global->mail_stream))) msgno = i; } } else if(mail_elt(ps_global->mail_stream,mn_m2raw(sp_msgmap(ps_global->mail_stream),i))->searched){ /* turn ON selectedness if "searched" bit is lit. */ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){ diff++; set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 1); if(hidden) set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 0); } } /* if we're zoomed and the current message was unselected */ if(narrow && msgno && get_lflag(ps_global->mail_stream,sp_msgmap(ps_global->mail_stream),mn_get_cur(sp_msgmap(ps_global->mail_stream)),MN_HIDE)) mn_reset_cur(sp_msgmap(ps_global->mail_stream), msgno); } Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE); } return(TCL_OK); } Tcl_SetResult(interp, "Can't read select option", TCL_STATIC); return(TCL_ERROR); } int peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { /* * Args: [broad | narrow] firstnumber lastnumber */ long first = 0L, last = 0L, n; if(objc == 2){ if(Tcl_GetLongFromObj(interp, objv[0], &first) == TCL_OK && Tcl_GetLongFromObj(interp, objv[1], &last) == TCL_OK){ if(last && last < first){ n = last; last = first; first = n; } if(first >= 1L && first <= mn_get_total(sp_msgmap(ps_global->mail_stream))){ if(last){ if(last >= 1L && last <= mn_get_total(sp_msgmap(ps_global->mail_stream))){ for(n = first; n <= last; n++) mm_searched(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n)); } else return(peSelectError(interp, "last out of range")); } else{ mm_searched(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), first)); } } else return(peSelectError(interp, "first out of range")); } else return(peSelectError(interp, "can't read first/last")); } else return(peSelectError(interp, "num first last")); return(TCL_OK); } int peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { /* * Args: [broad | narrow] * tense - "on", "since", "before" * year - 4 digit year * month - abbreviated month "jan", "feb"... * day - day number */ char *tense, *year, *month, *day, buf[256]; if(objc == 4){ if((tense = peSelValTense(objv[0])) != NULL){ if((year = peSelValYear(objv[1])) != NULL){ if((month = peSelValMonth(objv[2])) != NULL){ if((day = peSelValDay(objv[3])) != NULL){ snprintf(buf, sizeof(buf), "%s %s-%s-%s", tense, day, month, year); pine_mail_search_full(ps_global->mail_stream, NULL, mail_criteria(buf), SE_NOPREFETCH | SE_FREE); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "date tense year monthabbrev daynum")); return(TCL_OK); } int peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { /* * Args: [broad | narrow] * case - in not * field - to from cc recip partic subj any * text - free text search string */ int not; char field, *text; if(objc == 3){ if((not = peSelValCase(objv[0])) >= 0){ if((field = peSelValField(objv[1])) != '\0'){ if((text = Tcl_GetStringFromObj(objv[2], NULL)) && strlen(text) < 1024){ /* BUG: fix charset not to be NULL below */ if(agg_text_select(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), field, NULL, not, 0, text, NULL, NULL)) /* BUG: plug in "charset" above? */ return(peSelectError(interp, "programmer botch")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "text case field text")); return(TCL_OK); } int peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { /* * Args: [broad | narrow] * case - on not * status - imp new ans del */ int not; char flag; if(objc == 2){ if((not = peSelValCase(objv[0])) >= 0){ if((flag = peSelValFlag(objv[1])) != '\0'){ if(agg_flag_select(ps_global->mail_stream, not, flag, NULL)) return(peSelectError(interp, "programmer botch")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "")); } else return(peSelectError(interp, "status focus case flag")); return(TCL_OK); } char * peSelValTense(Tcl_Obj *objp) { char *tense, **pp; if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){ static char *tenses[] = {"on", "since", "before", NULL}; for(pp = tenses; *pp; pp++) if(!strucmp(*pp, tense)) return(tense); } return(NULL); } char * peSelValYear(Tcl_Obj *objp) { char *year; return((year = Tcl_GetStringFromObj(objp, NULL)) && strlen(year) == 4 && isdigit((unsigned char) year[0]) && isdigit((unsigned char) year[0]) && isdigit((unsigned char) year[0]) ? year : NULL); } char * peSelValMonth(Tcl_Obj *objp) { char *month, **pp; static char *mons[] = {"jan","feb","mar","apr", "may","jun","jul","aug", "sep","oct","nov","dec", NULL}; if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3) for(pp = mons; *pp; pp++) if(!strucmp(month, *pp)) return(*pp); return(NULL); } char * peSelValDay(Tcl_Obj *objp) { char *day; return(((day = Tcl_GetStringFromObj(objp, NULL)) && (day[0] == '0' || day[0] == '1' || day[0] == '2' || day[0] == '3') && isdigit((unsigned char) day[1]) && day[2] == '\0') ? day : NULL); } int peSelValCase(Tcl_Obj *objp) { char *not; if((not = Tcl_GetStringFromObj(objp, NULL)) != NULL){ if(!strucmp(not, "ton")) return(0); else if(!strucmp(not, "not")) return(1); } return(-1); } int peSelValField(Tcl_Obj *objp) { char *field; int i; static struct { char *field; int type; } fields[] = {{"from", 'f'}, {"to", 't'}, {"cc", 'c'}, {"subj", 's'}, {"any", 'a'}, {"recip", 'r'}, {"partic", 'p'}, {"body", 'b'}, {NULL,0}}; if((field = Tcl_GetStringFromObj(objp, NULL)) != NULL) for(i = 0; fields[i].field ; i++) if(!strucmp(fields[i].field, field)) return(fields[i].type); return(0); } int peSelValFlag(Tcl_Obj *objp) { char *flag; int i; static struct { char *flag; int type; } flags[] = {{"imp", '*'}, {"new", 'n'}, {"ans", 'a'}, {"del", 'd'}, {NULL,0}}; if((flag = Tcl_GetStringFromObj(objp, NULL)) != NULL) for(i = 0; flags[i].flag ; i++) if(!strucmp(flags[i].flag, flag)) return(flags[i].type); return(0); } int peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag) { int rv = 0; long i, n; char *range; /* * CMD: searched [before | after] # * * Returns: 1 if criteria is true, 0 otherwise */ if((range = Tcl_GetStringFromObj(objv[0], NULL)) && Tcl_GetLongFromObj(interp, objv[1], &n) != TCL_ERROR){ if(!strucmp(range, "before")){ for(i = 1L; i < n && i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++) if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){ rv = 1; break; } Tcl_SetResult(interp, int2string(rv), TCL_STATIC); return(TCL_OK); } else if(!strucmp(range, "after")){ for(i = n + 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++) if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){ rv = 1; break; } Tcl_SetResult(interp, int2string(rv), TCL_STATIC); return(TCL_OK); } } Tcl_SetResult(interp, "searched test failed", TCL_STATIC); return(TCL_ERROR); } int peSelectError(Tcl_Interp *interp, char *usage) { char buf[256]; snprintf(buf, sizeof(buf), "should be select %.128s", usage); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } int peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv) { char *subcmd; long n; if(!(n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT))){ Tcl_SetResult(interp, "No messages selected", TCL_STATIC); return(TCL_ERROR); } else if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){ if(objc == 1){ if(!strucmp(subcmd, "delete")){ /* BUG: is CmdWhere arg always right? */ (void) cmd_delete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL); Tcl_SetResult(interp, long2string(n), TCL_STATIC); return(TCL_OK); } else if(!strucmp(subcmd, "undelete")){ (void) cmd_undelete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT); Tcl_SetResult(interp, long2string(n), TCL_STATIC); return(TCL_OK); } } else if(objc == 2){ if(!strucmp(subcmd, "count")){ /* * Args: flag */ char *flagname; long n, rawno, count = 0; if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n); if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT) && peIsFlagged(ps_global->mail_stream, mail_uid(ps_global->mail_stream, rawno), flagname)){ count++; } } } Tcl_SetResult(interp, long2string(count), TCL_VOLATILE); return(TCL_OK); } } else if(objc == 3){ if(!strucmp(subcmd, "flag")){ /* * Args: case - on not * flag - imp new ans del */ char flag, *result; int not; long flagged; if((not = peSelValCase(objv[1])) >= 0){ if((flag = peSelValFlag(objv[2])) != '\0'){ result = peApplyFlag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), flag, not, &flagged); if(!result){ Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE); return(TCL_OK); } else return(peApplyError(interp, result)); } else return(peApplyError(interp, "invalid flag")); } else return(peApplyError(interp, "invalid case")); } else if(!strucmp(subcmd, "save")){ /* * Args: colid - * folder - imp new ans del */ int colid, flgs = 0, i; char *folder, *err; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){ if(!READONLY_FOLDER(ps_global->mail_stream) && F_OFF(F_SAVE_WONT_DELETE, ps_global)) flgs |= SV_DELETE; if(colid == 0 && !strucmp(folder, "inbox")) flgs |= SV_INBOXWOCNTXT; i = save(ps_global, ps_global->mail_stream, cp, folder, sp_msgmap(ps_global->mail_stream), flgs); err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem saving"; restore_selected(sp_msgmap(ps_global->mail_stream)); if(err) return(peApplyError(interp, err)); Tcl_SetResult(interp, long2string(i), TCL_VOLATILE); return(TCL_OK); } else return(peApplyError(interp, "can't select")); } else return(peApplyError(interp, "no folder name")); } return(peApplyError(interp, "bad colid")); } else return(peApplyError(interp, "invalid case")); } else if(!strucmp(subcmd, "copy")){ /* * Args: colid - * folder - imp new ans del */ int colid, flgs = 0, i; char *folder, *err; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){ if(colid == 0 && !strucmp(folder, "inbox")) flgs |= SV_INBOXWOCNTXT; i = save(ps_global, ps_global->mail_stream, cp, folder, sp_msgmap(ps_global->mail_stream), flgs); err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem copying"; restore_selected(sp_msgmap(ps_global->mail_stream)); if(err) return(peApplyError(interp, err)); Tcl_SetResult(interp, long2string(i), TCL_VOLATILE); return(TCL_OK); } else return(peApplyError(interp, "can't select")); } else return(peApplyError(interp, "no folder name")); } return(peApplyError(interp, "bad colid")); } else return(peApplyError(interp, "invalid case")); } else if(!strucmp(subcmd, "move")){ /* * Args: colid - * folder - imp new ans del */ int colid, flgs = 0, i; char *folder, *err; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid){ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){ flgs = SV_DELETE; if(colid == 0 && !strucmp(folder, "inbox")) flgs |= SV_INBOXWOCNTXT; i = save(ps_global, ps_global->mail_stream, cp, folder, sp_msgmap(ps_global->mail_stream), flgs); err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem moving"; restore_selected(sp_msgmap(ps_global->mail_stream)); if(err) return(peApplyError(interp, err)); Tcl_SetResult(interp, long2string(i), TCL_VOLATILE); return(TCL_OK); } else return(peApplyError(interp, "can't select")); } else return(peApplyError(interp, "no folder name")); } return(peApplyError(interp, "bad colid")); } else return(peApplyError(interp, "invalid case")); } else if(!strucmp(subcmd, "spam")){ /* * Args: spamaddr - * spamsubj - */ char *spamaddr, *spamsubj = NULL; long n, rawno; if((spamaddr = Tcl_GetStringFromObj(objv[1], NULL)) && (spamsubj = Tcl_GetStringFromObj(objv[2], NULL))){ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n); if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)){ char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL; if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){ Tcl_SetResult(interp, rs, TCL_VOLATILE); return(TCL_ERROR); } } } } Tcl_SetResult(interp, "OK", TCL_VOLATILE); return(TCL_OK); } } } return(peApplyError(interp, "unknown option")); } char * peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged) { char *seq, *flagstr; long flags, flagid; switch (flag) { case '*' : flagstr = "\\FLAGGED"; flags = not ? 0L : ST_SET; flagid = not ? F_FLAG : F_UNFLAG; break; case 'n' : flagstr = "\\SEEN"; flags = not ? ST_SET : 0L; flagid = not ? F_UNSEEN : F_SEEN; break; case 'a' : flagstr = "\\ANSWERED"; flags = not ? 0L : ST_SET; flagid = not ? F_ANS : F_UNANS; break; case 'd': flagstr = "\\DELETED"; flags = not ? 0L : ST_SET; flagid = not ? F_DEL : F_UNDEL; break; default : return("unknown flag"); break; } if(pseudo_selected(stream, msgmap)){ if((seq = currentf_sequence(stream, msgmap, flagid, flagged, 1, NULL, NULL)) != NULL){ mail_flag(stream, seq, flagstr, flags); fs_give((void **) &seq); } restore_selected(msgmap); return(NULL); } else return("can't select"); } int peApplyError(Tcl_Interp *interp, char *usage) { char buf[256]; snprintf(buf, sizeof(buf), "apply error: %.128s", usage); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } /* * peIndexFormat - Return with interp's result object set to * represent the index line's format as a list of * index-field-name, percentage-width pairs */ int peIndexFormat(Tcl_Interp *interp) { INDEX_COL_S *cdesc = NULL; char *name, wbuf[4], *dname; for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) { dname = NULL; switch(cdesc->ctype){ case iFStatus: case iIStatus: case iSIStatus: dname = "iStatus"; case iStatus: name = "Status"; break; case iMessNo: name = "Number"; break; case iPrio: case iPrioAlpha: case iPrioBang: name = "Priority"; break; case iDate: case iSDate: case iSTime: case iLDate: case iS1Date: case iS2Date: case iS3Date: case iS4Date: case iDateIso: case iDateIsoS: case iSDateIso: case iSDateIsoS: case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4: case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS: case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4: case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24: case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424: case iCurDate: case iCurDateIso: case iCurDateIsoS: case iCurTime24: case iCurTime12: case iCurPrefDate: name = "Date"; break; case iCurDay: case iCurDay2Digit: case iCurDayOfWeek: case iCurDayOfWeekAbb: name = "Day"; break; case iCurMon: case iCurMon2Digit: case iCurMonLong: case iCurMonAbb: name= "Month"; break; case iTime24: case iTime12: case iTimezone: case iCurPrefTime: name = "Time"; break; case iDay2Digit: case iDayOfWeek: case iDayOfWeekAbb: name = "Day"; break; case iMonAbb: case iMon2Digit: name = "Month"; break; case iYear: case iYear2Digit: case iCurYear: case iCurYear2Digit: name = "Year"; break; case iScore : name = "Score"; break; case iFromTo: case iFromToNotNews: case iFrom: name = "From"; break; case iTo: case iToAndNews : name = "To"; break; case iCc: name = "Cc"; break; case iRecips: name = "Recipients"; break; case iSender: name = "Sender"; break; case iSize : case iSizeComma : case iSizeNarrow : case iDescripSize: case iKSize : name = "Size"; break; case iAtt: name = "Attachments"; break; case iAddress : name = "Address"; break; case iMailbox : name = "Mailbox"; break; case iOpeningText: case iOpeningTextNQ: name = "Excerpt"; break; case iSubject : case iSubjKey : case iSubjKeyInit : case iSubjectText : case iSubjKeyText : case iShortSubject : case iShortSubjKey : case iSubjKeyInitText : case iShortSubjKeyInit : name = "Subject"; break; case iNews: case iNewsAndTo : name = "News"; break; case iNewsAndRecips: name = "News/Recip"; break; case iRecipsAndNews: name = "Recip/News"; break; default : name = ""; break; } if(cdesc->width > 0){ int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH); snprintf(wbuf, sizeof(wbuf), "%d%%", p); } else wbuf[0] = '\0'; if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK) return(TCL_ERROR); } return(TCL_OK); } int peNewMailResult(Tcl_Interp *interp) { unsigned long n, uid; if(sp_mail_box_changed(ps_global->mail_stream)){ if((n = sp_mail_since_cmd(ps_global->mail_stream)) != 0L){ /* first element is count of new messages */ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(n)) != TCL_OK) return(TCL_ERROR); /* second element is UID of most recent message */ for(uid = ps_global->mail_stream->nmsgs; uid > 1L; uid--) if(!get_lflag(ps_global->mail_stream, NULL, uid, MN_EXLD)) break; if(!uid){ Tcl_ResetResult(interp); Tcl_SetResult(interp, "0 0 0", TCL_STATIC); return(TCL_ERROR); } uid = mail_uid(ps_global->mail_stream, uid); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(uid)) != TCL_OK) return(TCL_ERROR); } else { if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(0)) != TCL_OK) return(TCL_ERROR); /* zero is UID of new message */ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(0)) != TCL_OK) return(TCL_ERROR); } /* third element is expunge count */ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(sp_expunge_count(ps_global->mail_stream) ? sp_expunge_count(ps_global->mail_stream) : 0L)) != TCL_OK) return(TCL_ERROR); } else Tcl_SetResult(interp, "0 0 0", TCL_STATIC); return(TCL_OK); } /* * * * * * * * Start of Per-Thread/SubThread access functions * * * * * * * */ /* * PEThreadCmd - access/manipulate various pieces of thread state */ int PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err, errbuf[256], *cmd, *op; long uidl; imapuid_t uid; dprint((2, "PEThreadCmd")); snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request", Tcl_GetStringFromObj(objv[0], NULL)); if(!(ps_global && ps_global->mail_stream)){ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox", Tcl_GetStringFromObj(objv[0], NULL)); } else if(objc < 2){ Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?"); } else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){ return(TCL_ERROR); /* conversion problem? */ } else if(!peSequenceNumber(uidl)){ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist", Tcl_GetStringFromObj(objv[0], NULL), uidl); } else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ uid = uidl; if(objc == 3){ if(!strucmp(cmd,"info")){ #define WP_MAX_THRD_PREFIX 256 long raw; PINETHRD_S *pthrd; char tstr[WP_MAX_THRD_PREFIX]; if((raw = peSequenceNumber(uid)) != 0L){ /* * translate PINETHRD_S data into */ if((pthrd = msgno_thread_info(ps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){ tstr[0] = '\0'; /* BUG: build tstr form pthrd */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tstr, -1)); } } else Tcl_SetResult(interp, "0", TCL_STATIC); return(TCL_OK); } } else if(objc == 5){ if(!strucmp(cmd,"flag")){ if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){ if(!strucmp(op,"deleted")){ int value; if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){ long n; PINETHRD_S *pthrd; char *flag; while(1){ if(!(n = peSequenceNumber(uid))){ Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC); return(TCL_ERROR); } flag = cpystr("\\DELETED"); mail_flag(ps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L)); fs_give((void **) &flag); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(uid), -1)); if(++n <= ps_global->mail_stream->nmsgs){ uid = mail_uid(ps_global->mail_stream, n); } else break; if((pthrd = msgno_thread_info(ps_global->mail_stream, n, NULL,THD_TOP)) != NULL){ } else break; } } } } } } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * * * * * * * Start of Per-Message access functions * * * * * * * */ static struct _message_cmds { char *cmd; int hcount; struct { int argcount; int (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **); } h[3]; } message_cmds[] = { {"size", 1, {{3, peMessageSize}}}, {"date", 2, {{3, peMessageDate}, {4, peMessageDate}}}, {"subject", 1, {{3, peMessageSubject}}}, {"fromaddr", 1, {{3, peMessageFromAddr}}}, {"toaddr", 1, {{3, peMessageToAddr}}}, {"ccaddr", 1, {{3, peMessageCcAddr}}}, {"status", 1, {{3, peMessageStatus}}}, {"statusbits", 1, {{3, peMessageStatusBits}}}, {"charset", 1, {{3, peMessageCharset}}}, {"number", 1, {{3, peMsgnoFromUID}}}, {"envelope", 0}, {"rawenvelope", 0}, {"text", 1, {{3, peMessageText}}}, {"header", 1, {{3, peMessageHeader}}}, {"attachments", 1, {{3, peMessageAttachments}}}, {"body", 3, {{3, peMessageBody}, {4, peMessageBody}}}, {"cid", 1, {{4, peMessagePartFromCID}}}, {"flag", 2, {{4, peGetFlag}, {5, peSetFlag}}}, {"replyheaders", 2, {{3, peReplyHeaders},{4, peReplyHeaders}}}, {"replytext", 2, {{4, peReplyText}, {5, peReplyText}}}, {"forwardheaders", 2, {{3, peForwardHeaders}, {4, peForwardHeaders}}}, {"forwardtext", 2, {{3, peForwardText}, {4, peForwardText}}}, {"rawbody", 0}, {"select", 2, {{3, peMsgSelect}, {4, peMsgSelect}}}, {"detach", 1, {{5, peDetach}}}, {"attachinfo", 1, {{4, peAttachInfo}}}, {"savedefault", 1, {{3, peSaveDefault}}}, {"save", 1, {{5, peSave}}}, {"copy", 1, {{5, peCopy}}}, {"move", 1, {{5, peMove}}}, {"takeaddr", 1, {{3, peTakeaddr}}}, {"takefrom", 1, {{3, peTakeFrom}}}, {"replyquote", 1, {{3, peReplyQuote}}}, {"bounce", 2, {{4, peMessageBounce},{5, peMessageBounce}}}, {"spam", 1, {{5, peMessageSpamNotice}}}, {"needpasswd", 1, {{3, peMessageNeedPassphrase}}}, {NULL, 0} }; /* * PEMessageCmd - export various bits of message information * * NOTE: all exported commands are of the form: * * PEMessage */ int PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err, errbuf[256], *cmd; int i, j; long uidl; imapuid_t uid; dprint((5, "PEMessageCmd")); snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request", Tcl_GetStringFromObj(objv[0], NULL)); if(!(ps_global && ps_global->mail_stream)){ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox", Tcl_GetStringFromObj(objv[0], NULL)); } else if(objc < 3){ Tcl_WrongNumArgs(interp, 0, objv, "PEMessage cmd ?args?"); } else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){ return(TCL_ERROR); /* conversion problem? */ } else if(!peMessageNumber(uidl)){ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist", Tcl_GetStringFromObj(objv[0], NULL), uidl); } else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ uid = uidl; for(i = 0; message_cmds[i].cmd; i++) if(!strcmp(cmd, message_cmds[i].cmd)){ for(j = 0; j < message_cmds[i].hcount; j++) if(message_cmds[i].h[j].argcount == objc) return((*message_cmds[i].h[j].f)(interp, uid, objc - 3, &((Tcl_Obj **)objv)[3])); snprintf(err = errbuf, sizeof(errbuf), "PEMessage: %s: mismatched argument count", cmd); break; } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * return the uid's ordinal number within the CURRENT SORT */ long peMessageNumber(imapuid_t uid) { return(mn_raw2m(sp_msgmap(ps_global->mail_stream), peSequenceNumber(uid))); } /* * return the uid's RAW message number (for c-client reference, primarily) */ long peSequenceNumber(imapuid_t uid) { return(mail_msgno(ps_global->mail_stream, uid)); } int peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { long raw; if((raw = peSequenceNumber(uid)) && pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL)){ Tcl_SetResult(interp, long2string(mail_elt(ps_global->mail_stream, raw)->rfc822_size), TCL_VOLATILE); } else Tcl_SetResult(interp, "0", TCL_STATIC); return(TCL_OK); } int peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *cmd; long raw; ENVELOPE *env; MESSAGECACHE mc; if((raw = peSequenceNumber(uid)) && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){ if(objc == 1 && objv[0]){ if(mail_parse_date(&mc, env->date)){ if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){ if(!strucmp(cmd,"day")){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(cmd,"month")){ Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE); return(TCL_OK); } else if(!strucmp(cmd,"year")){ Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE); return(TCL_OK); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); } } else Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC); } else Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC); } else{ Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE); return(TCL_OK); } } else Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC); return(TCL_ERROR); } int peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peMessageField(interp, uid, "from")); } int peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peMessageField(interp, uid, "to")); } int peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peMessageField(interp, uid, "cc")); } int peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peMessageField(interp, uid, "subject")); } int peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field) { long raw; char *s = ""; ENVELOPE *env; if((raw = peSequenceNumber(uid)) && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){ if(!strucmp(field, "from")){ if(env->from && env->from->mailbox) snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox, (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : ""); } else if(!strucmp(field, "to")){ if(env->to && env->to->mailbox) snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox, (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : ""); } else if(!strucmp(field, "cc")){ if(env->cc && env->cc->mailbox) snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox, (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : ""); } else if(!strucmp(field, "subject")){ if(env->subject) snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject); } else{ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } Tcl_SetResult(interp, s, TCL_VOLATILE); return(TCL_OK); } Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC); return(TCL_ERROR); } int peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { long raw; MESSAGECACHE *mc; if((raw = peSequenceNumber(uid)) != 0L){ if(!((mc = mail_elt(ps_global->mail_stream, raw)) && mc->valid)){ mail_fetch_flags(ps_global->mail_stream, ulong2string(uid), FT_UID); mc = mail_elt(ps_global->mail_stream, raw); } if (mc->deleted) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Deleted", -1)); if (mc->answered) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Answered", -1)); if (!mc->seen) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("New", -1)); if (mc->flagged) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Important", -1)); } return(TCL_OK); } int peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { /* everything coming out of pith better be utf-8 */ Tcl_SetResult(interp, "UTF-8", TCL_STATIC); return(TCL_OK); } int peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { #ifdef SMIME return((ps_global && ps_global->smime && ps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR); #else return(TCL_ERROR); #endif /* SMIME */ } int peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE); return(TCL_OK); } /* * peInterpWritec - collect filtered output, appending to the * command's result list on each EOL */ int peInterpWritec(int c) { unsigned char ch = (unsigned char) (0xff & c); if(ch == '\n') return(peInterpFlush() == TCL_OK); else so_writec(ch, peED.store); return(1); } /* * peInterpFlush - write accumulated line to result object mapping * embedded data into exportable tcl list members * */ int peInterpFlush(void) { char *line, *p, *tp, *tp2, col1[32], col2[32]; Tcl_Obj *lobjp, *objColor, *objPair; line = (char *) so_text(peED.store); if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){ if((p = strindex(line, TAG_EMBED)) != NULL){ do{ *p = '\0'; if(p - line) peAppListF(peED.interp, lobjp, "%s%s", "t", line); switch(*++p){ case TAG_HANDLE : { int i, n; HANDLE_S *h; for(n = 0, i = *++p; i > 0; i--) n = (n * 10) + (*++p - '0'); line = ++p; /* prepare for next section of line */ if(!peED.inhandle){ peED.inhandle = 1; if((h = get_handle(peED.handles, n)) != NULL) switch(h->type){ case IMG : { Tcl_Obj *llObj, *rObj; llObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1)); rObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1)); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1)); Tcl_ListObjAppendElement(peED.interp, llObj, rObj); Tcl_ListObjAppendElement(peED.interp, lobjp, llObj); peED.inhandle = 0; } break; case URL : { Tcl_Obj *llObj, *rObj; llObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1)); rObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1)); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1)); Tcl_ListObjAppendElement(peED.interp, llObj, rObj); Tcl_ListObjAppendElement(peED.interp, lobjp, llObj); } break; case Attach : { Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj; alObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1)); peGetMimeTyping(mail_body(ps_global->mail_stream, peSequenceNumber(peED.uid), (unsigned char *) h->h.attach->number), &tObj, &stObj, &fnObj, &eObj); rObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid)); Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1)); Tcl_ListObjAppendElement(peED.interp, rObj, tObj); Tcl_ListObjAppendElement(peED.interp, rObj, stObj); Tcl_ListObjAppendElement(peED.interp, rObj, fnObj); Tcl_ListObjAppendElement(peED.interp, rObj, eObj); Tcl_ListObjAppendElement(peED.interp, alObj, rObj); Tcl_ListObjAppendElement(peED.interp, lobjp, alObj); } break; default : break; } } } break; case TAG_FGCOLOR : if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){ /* look ahead */ if(p[11] == TAG_EMBED && p[12] == TAG_BGCOLOR && (tp2 = peColorStr(p + 13, col2))){ objColor = Tcl_NewListObj(0, NULL); objPair = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1)); Tcl_ListObjAppendElement(peED.interp, objColor, objPair); Tcl_ListObjAppendElement(peED.interp, lobjp, objColor); strcpy(peED.color.bg, tp2); p += 13; } else if(strcmp(peED.color.bg, peED.color.bgdef)){ objColor = Tcl_NewListObj(0, NULL); objPair = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1)); Tcl_ListObjAppendElement(peED.interp, objColor, objPair); Tcl_ListObjAppendElement(peED.interp, lobjp, objColor); strcpy(peED.color.bg, peED.color.bgdef); } else peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp); strcpy(peED.color.fg, tp); } line = p + 11; break; case TAG_BGCOLOR : if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){ /* look ahead */ if(p[11] == TAG_EMBED && p[12] == TAG_FGCOLOR && (tp2 = peColorStr(p + 13, col2))){ objColor = Tcl_NewListObj(0, NULL); objPair = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1)); Tcl_ListObjAppendElement(peED.interp, objColor, objPair); Tcl_ListObjAppendElement(peED.interp, lobjp, objColor); strcpy(peED.color.fg, tp2); p += 13; } else if(strcmp(peED.color.fg, peED.color.fgdef)){ objColor = Tcl_NewListObj(0, NULL); objPair = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1)); Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1)); Tcl_ListObjAppendElement(peED.interp, objColor, objPair); Tcl_ListObjAppendElement(peED.interp, lobjp, objColor); strcpy(peED.color.fg, peED.color.fgdef); } else peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp); strcpy(peED.color.bg, tp); } line = p + 11; break; case TAG_ITALICON : peAppListF(peED.interp, lobjp, "%s%s", "italic", "on"); line = p + 1; break; case TAG_ITALICOFF : peAppListF(peED.interp, lobjp, "%s%s", "italic", "off"); line = p + 1; break; case TAG_BOLDON : peAppListF(peED.interp, lobjp, "%s%s", "bold", "on"); line = p + 1; break; case TAG_BOLDOFF : peAppListF(peED.interp, lobjp, "%s%s", "bold", "off"); line = p + 1; break; case TAG_ULINEON : peAppListF(peED.interp, lobjp, "%s%s", "underline", "on"); line = p + 1; break; case TAG_ULINEOFF : peAppListF(peED.interp, lobjp, "%s%s", "underline", "off"); line = p + 1; break; case TAG_STRIKEON : peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on"); line = p + 1; break; case TAG_STRIKEOFF : peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off"); line = p + 1; break; case TAG_BIGON : peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on"); line = p + 1; break; case TAG_BIGOFF : peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off"); line = p + 1; break; case TAG_SMALLON : peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on"); line = p + 1; break; case TAG_SMALLOFF : peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off"); line = p + 1; break; case TAG_INVOFF : case TAG_HANDLEOFF : if(peED.inhandle){ peAppListF(peED.interp, lobjp, "%s%s", "urlend", ""); peED.inhandle = 0; } /* fall thru and advance "line" */ default : line = p + 1; break; } } while((p = strindex(line, TAG_EMBED)) != NULL); if(*line) peAppListF(peED.interp, lobjp, "%s%s", "t", line); } else peAppListF(peED.interp, lobjp, "%s%s", "t", line); } else peAppListF(peED.interp, lobjp, "%s%s", "t", ""); if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){ so_truncate(peED.store, 0L); return(TCL_OK); } return(TCL_ERROR); } /* * peInterpWritec - collect filtered output, appending to the * command's result list on each EOL */ int peNullWritec(int c) { return(1); } char * peColorStr(char *s, char *b) { int i, j, color; i = 0; b[0] = '\0'; while(1){ color = 0; for(j = 0; j < 3; j++, s++) if(isdigit((unsigned char) *s)) color = (color * 10) + (*s - '0'); s++; /* advance past ',' */ if(color < 256) sprintf(b + strlen(b), "%2.2x", color); else break; if(++i == 3) return(b); } return(NULL); } /* * returns a list of elements */ int peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { MESSAGECACHE *mc; HEADER_S h; int flags, rv = TCL_OK; long raw; #if 0 char *color; #endif /* * ONLY full header mode (raw) output should get written to the * writec function we pass format_header. If there's something * in the store after formatting ,we'll write it to the Tcl result * then, not as its accumulated */ peED.interp = interp; peED.obj = Tcl_NewStringObj("", -1); if(peED.store) so_seek(peED.store, 0L, 0); else peED.store = so_get(CharStar, NULL, EDIT_ACCESS); flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED; #if 0 peED.color.fg[0] = '\0'; if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_FGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf)); } peED.color.bg[0] = '\0'; if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_BGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf)); } peInterpFlush(); #endif raw = peSequenceNumber(uid); if(peED.uid != uid){ peED.uid = uid; peED.body = NULL; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body)) && (mc = mail_elt(ps_global->mail_stream, raw)))){ char buf[256]; snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"); dprint((1, "ERROR fetching %s of msg %ld: %s", peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate")); Tcl_SetResult(interp, buf, TCL_VOLATILE); rv = TCL_ERROR; } else{ zero_atmts(ps_global->atmts); #ifdef SMIME if(ps_global && ps_global->smime && ps_global->smime->need_passphrase) ps_global->smime->need_passphrase = 0; fiddle_smime_message(peED.body, raw); #endif describe_mime(peED.body, "", 1, 1, 0, flags); } } /* NO HANDLES init_handles(&peED.handles);*/ /* * Collect header pieces into lists via the passed custom formatter. Collect * everything else in the storage object passed. The latter should only end up * with raw header data. * * BUG: DEAL WITH COLORS */ if(rv == TCL_OK){ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT); if(format_header(ps_global->mail_stream, raw, NULL, peED.env, &h, NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){ char buf[256]; snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid)); dprint((1, buf)); Tcl_SetResult(interp, buf, TCL_VOLATILE); rv = TCL_ERROR; } } peInterpFlush(); peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj); so_give(&peED.store); return(rv); } void peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc, long int which, char *oacs, int flags) { char *p2, buftmp[MAILTMPLEN]; Tcl_Obj *objHdr; if(!e) return; if((which & FE_DATE) && e->date) { if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){ snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date); buftmp[sizeof(buftmp)-1] = '\0'; p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp); peFormatEnvelopeText("Date", p2); } /* BUG: how does error feedback bubble back up? */ } if((which & FE_FROM) && e->from) peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc); if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from))) peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc); if((which & FE_TO) && e->to) peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc); if((which & FE_CC) && e->cc) peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc); if((which & FE_BCC) && e->bcc) peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc); if((which & FE_RETURNPATH) && e->return_path) peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc); if((which & FE_NEWSGROUPS) && e->newsgroups) peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc); if((which & FE_FOLLOWUPTO) && e->followup_to) peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc); if((which & FE_SUBJECT) && e->subject && e->subject[0]){ if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){ char *freeme = NULL; p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000); if(flags & FM_DISPLAY && (ps_global->display_keywords_in_subject || ps_global->display_keywordinits_in_subject)){ /* don't bother if no keywords are defined */ if(some_user_flags_defined(s)) p2 = freeme = prepend_keyword_subject(s, n, p2, ps_global->display_keywords_in_subject ? KW : KWInit, NULL, ps_global->VAR_KW_BRACES); } peFormatEnvelopeText("Subject", p2); if(freeme) fs_give((void **) &freeme); } } if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from))) peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc); if((which & FE_MESSAGEID) && e->message_id){ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000); peFormatEnvelopeText("Message-ID", p2); } if((which & FE_INREPLYTO) && e->in_reply_to){ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000); peFormatEnvelopeText("In-Reply-To", p2); } if((which & FE_REFERENCES) && e->references) { p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000); peFormatEnvelopeText("References", p2); } } /* * appends caller's result with: {"text" field_name {field_value}} */ void peFormatEnvelopeText(char *field_name, char *field_value) { peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value); } /* * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }} * {"rawaddr" field_name {{raw_address} ... }} */ void peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name, struct mail_address *addr, int flags, char *oacs, gf_io_t pc) { char *ptmp, *mtmp, *atype = "addr"; int group = 0; ADDRESS *atmp; Tcl_Obj *objAddrList = NULL; STORE_S *tso; gf_io_t tpc; extern const char *rspecials; extern const char *rspecials_minus_quote_and_dot; if(!addr) return; /* * quickly run down address list to make sure none are patently bogus. * If so, just blat raw field out. */ for(atmp = addr; stream && atmp; atmp = atmp->next) if(atmp->host && atmp->host[0] == '.'){ char *field, *fields[2]; atype = "rawaddr"; if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL) return; /* BUG: handle list creation failure */ fields[1] = NULL; fields[0] = cpystr(field_name); if((ptmp = strchr(fields[0], ':')) != NULL) *ptmp = '\0'; if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){ char *h, *t; for(t = h = field; *h ; t++) if(*t == '\015' && *(t+1) == '\012'){ *t = '\0'; /* tie off line */ Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1)); if(!*(h = (++t) + 1)) /* set new h and skip CRLF */ break; /* no more to write */ } else if(!*t){ /* shouldn't happen much */ if(h != t) Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1)); break; } fs_give((void **)&field); } fs_give((void **)&fields[0]); } if(!objAddrList){ if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL) return; /* BUG: handle list creation failure */ gf_set_so_writec(&tpc, tso); while(addr){ atmp = addr->next; /* remember what's next */ addr->next = NULL; if(!addr->host && addr->mailbox){ mtmp = addr->mailbox; addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8( (unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, addr->mailbox)); } ptmp = addr->personal; /* RFC 1522 personal name? */ addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000); tmp_20k_buf[10000-1] = '\0'; /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */ if (addr->host) { /* ordinary address? */ if (!(addr->personal || addr->adl)){ so_seek(tso, 0L, 0); pine_rfc822_address (addr, tpc); peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso))); } else { /* no, must use phrase form */ Tcl_Obj *objTmp; if (addr->personal){ so_seek(tso, 0L, 0); pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc); objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)); } so_seek(tso, 0L, 0); pine_rfc822_address(addr, tpc); peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso))); } if(group) group++; } else if (addr->mailbox) { /* start of group? */ so_seek(tso, 0L, 0); /* yes, write group name */ pine_rfc822_cat (addr->mailbox, rspecials, tpc); peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), ""); group = 1; /* in a group */ } else if (group) { /* must be end of group (but be paranoid) */ peAppListF(peED.interp, objAddrList, "%s%s", "", ";"); group = 0; /* no longer in that group */ } addr->personal = ptmp; /* restore old personal ptr */ if(!addr->host && addr->mailbox){ fs_give((void **)&addr->mailbox); addr->mailbox = mtmp; } addr->next = atmp; addr = atmp; } gf_clear_so_writec(tso); so_give(&tso); } peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList); } /* * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }} */ void peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_io_t pc) { char buf[MAILTMPLEN]; int llen; char *next_ng; Tcl_Obj *objNewsgroups; /* BUG: handle list creation failure */ if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL) return; llen = strlen(field_name); while(*newsgrps){ for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++) ; strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1)); buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0'; Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1)); newsgrps = next_ng; if(*newsgrps) newsgrps++; } peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups); } int peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { MESSAGECACHE *mc; ATTACH_S *a; BODY *body; Tcl_Obj *objAtt, *tObj, *stObj, *fnObj; int flags, rv = TCL_OK; long raw; peED.interp = interp; peED.obj = Tcl_GetObjResult(interp); flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER; raw = peSequenceNumber(uid); if(peED.uid != uid){ memset(&peED, 0, sizeof(peED)); peED.uid = uid; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body)) && (mc = mail_elt(ps_global->mail_stream, raw)))){ char buf[256]; snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"); dprint((1, "ERROR fetching %s of msg %ld: %s", peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate")); Tcl_SetResult(interp, buf, TCL_VOLATILE); rv = TCL_ERROR; } else{ zero_atmts(ps_global->atmts); #ifdef SMIME if(ps_global && ps_global->smime && ps_global->smime->need_passphrase) ps_global->smime->need_passphrase = 0; fiddle_smime_message(peED.body, raw); #endif describe_mime(peED.body, "", 1, 1, 0, flags); } } /* package up attachment list */ for(a = ps_global->atmts; rv == TCL_OK && a->description != NULL; a++) if((objAtt = Tcl_NewListObj(0, NULL)) != NULL && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL); if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK)) rv = TCL_ERROR; } else rv = TCL_ERROR; return(rv); } int peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { MESSAGECACHE *mc; int flags, rv = TCL_OK; long raw; char *color; peED.interp = interp; peED.obj = Tcl_GetObjResult(interp); if(peED.store) so_seek(peED.store, 0L, 0); else peED.store = so_get(CharStar, NULL, EDIT_ACCESS); flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED; if(objc == 1 && objv[0]){ /* flags */ int i, nFlags; Tcl_Obj **objFlags; char *flagstr; Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags); for(i = 0; i < nFlags; i++){ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){ rv = TCL_ERROR; } if(!strucmp(flagstr, "html")) flags |= (FM_HTML | FM_HIDESERVER); else if(!strucmp(flagstr, "images")) flags |= (FM_HTMLIMAGES); } } peED.color.fg[0] = '\0'; if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_FGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf)); } peED.color.bg[0] = '\0'; if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_BGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf)); } peInterpFlush(); init_handles(&peED.handles); raw = peSequenceNumber(uid); if(peED.uid != uid){ peED.uid = uid; peED.body = NULL; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body)) && (mc = mail_elt(ps_global->mail_stream, raw)))){ char buf[256]; snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"); dprint((1, "ERROR fetching %s of msg %ld: %s", peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate")); Tcl_SetResult(interp, buf, TCL_VOLATILE); rv = TCL_ERROR; } else{ zero_atmts(ps_global->atmts); #ifdef SMIME if(ps_global && ps_global->smime && ps_global->smime->need_passphrase) ps_global->smime->need_passphrase = 0; fiddle_smime_message(peED.body, raw); #endif describe_mime(peED.body, "", 1, 1, 0, flags); } } /* format message body */ if(rv == TCL_OK){ HEADER_S h; char *errstr; HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT); #ifdef SMIME /* kind of a hack, the description maybe shouldn't be in the editorial stuff */ if(ps_global->smime && ps_global->smime->need_passphrase) flags &= ~FM_NOEDITORIAL; #endif if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){ gf_puts(errstr, peInterpWritec); rv = TCL_ERROR; } } peInterpFlush(); so_give(&peED.store); free_handles(&peED.handles); return(rv); } int peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { MESSAGECACHE *mc; ENVELOPE *env; BODY *body; int flags; long raw; char *color; memset(&peED, 0, sizeof(peED)); peED.interp = interp; peED.obj = Tcl_GetObjResult(interp); peED.store = so_get(CharStar, NULL, EDIT_ACCESS); peED.color.fg[0] = '\0'; if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_FGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf)); } peED.color.bg[0] = '\0'; if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){ peInterpWritec(TAG_EMBED); peInterpWritec(TAG_BGCOLOR); gf_puts(color, peInterpWritec); strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf)); } raw = peSequenceNumber(peED.uid = uid); body = NULL; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; if(!((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body)) && (mc = mail_elt(ps_global->mail_stream, raw)))){ char buf[256]; snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"); dprint((1, "ERROR fetching %s of msg %ld: %s", env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)), ps_global->last_error[0] ? ps_global->last_error : "Indeterminate")); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED; init_handles(&peED.handles); (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec); peInterpFlush(); so_give(&peED.store); free_handles(&peED.handles); return(TCL_OK); } /* * peMessagePartFromCID - return part number assoc'd with given uid and CID */ int peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *cid, sect_buf[256]; long raw; ENVELOPE *env; BODY *body; raw = peSequenceNumber(peED.uid = uid); ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){ if((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body)) != NULL){ sect_buf[0] = '\0'; if(peLocateBodyByCID(cid, sect_buf, body)){ Tcl_SetResult(interp, sect_buf, TCL_VOLATILE); } } else{ Tcl_SetResult(interp, ps_global->last_error[0] ? ps_global->last_error : "Error getting CID", TCL_VOLATILE); return(TCL_ERROR); } } return(TCL_OK); } int peLocateBodyByCID(char *cid, char *section, BODY *body) { if(body->type == TYPEMULTIPART){ char subsection[256], *subp; int n; PART *part = body->nested.part; if(!(part = body->nested.part)) return(0); subp = subsection; if(section && *section){ for(n = 0; n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++) ; *subp++ = '.'; } n = 1; do { sprintf(subp, "%d", n++); if(peLocateBodyByCID(cid, subsection, &part->body)){ strcpy(section, subsection); return(1); } } while((part = part->next) != NULL); return(0); } return((body && body->id) ? !strcmp(cid, body->id) : 0); } /* * peGetFlag - Return 1 or 0 based on requested flags current state * * Params: argv[0] == flagname */ int peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *flagname; Tcl_SetResult(interp, int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL) ? peIsFlagged(ps_global->mail_stream, uid, flagname) : 0), TCL_VOLATILE); return(TCL_OK); } int peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname) { MESSAGECACHE *mc; long raw = peSequenceNumber(uid); if(!((mc = mail_elt(stream, raw)) && mc->valid)){ mail_fetch_flags(stream, ulong2string(uid), FT_UID); mc = mail_elt(stream, raw); } if(!strucmp(flagname, "deleted")) return(mc->deleted); if(!strucmp(flagname, "new")) return(!mc->seen); if(!strucmp(flagname, "important")) return(mc->flagged); if(!strucmp(flagname, "answered")) return(mc->answered); if(!strucmp(flagname, "recent")) return(mc->recent); return(0); } /* * peSetFlag - Set requested flags value to 1 or 0 * * Params: abjv[0] == flagname * objv[1] == newvalue */ int peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *flagname, *flagstr = NULL; int value; if((flagname = Tcl_GetStringFromObj(objv[0], NULL)) && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){ if(!strucmp(flagname, "deleted")){ flagstr = "\\DELETED"; } else if(!strucmp(flagname, "new")){ flagstr = "\\SEEN"; value = !value; } else if(!strucmp(flagname, "important")){ flagstr = "\\FLAGGED"; } else if(!strucmp(flagname, "answered")){ flagstr = "\\ANSWERED"; } else if(!strucmp(flagname, "recent")){ flagstr = "\\RECENT"; } if(flagstr){ ps_global->c_client_error[0] = '\0'; mail_flag(ps_global->mail_stream, ulong2string(uid), flagstr, (value ? ST_SET : 0L) | ST_UID); if(ps_global->c_client_error[0] != '\0'){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s", ps_global->c_client_error); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } } } Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC); return(TCL_OK); } /* * peMsgSelect - Return 1 or 0 based on whether given UID is selected * * Params: argv[0] == selected */ int peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { int value; if(objc == 1 && objv[0]){ if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){ if(value){ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), MN_SLCT, 1); set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), MN_HIDE, 0); } else { set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), MN_SLCT, 0); /* if zoomed, lite hidden bit */ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)) set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), MN_HIDE, 1); } } else{ Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC); return(TCL_ERROR); } } Tcl_SetResult(interp, (get_lflag(ps_global->mail_stream, NULL, peSequenceNumber(uid), MN_SLCT)) ? "1" : "0", TCL_VOLATILE); return(TCL_OK); } /* * peAppendIndexParts - append list of digested index pieces to given object * * Params: * */ int peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched) { Tcl_Obj *objField, *objElement, *objp; ICE_S *h; IFIELD_S *f; IELEM_S *ie; if((h = build_header_work(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), gPeITop, gPeICount, fetched)) != NULL){ for(f = h->ifield; f; f = f->next){ if((objField = Tcl_NewListObj(0, NULL)) == NULL) return(TCL_ERROR); for(ie = f->ielem; ie ; ie = ie->next){ if((objElement = Tcl_NewListObj(0, NULL)) == NULL) return(TCL_ERROR); if(ie->datalen){ /* FIRST: DATA */ #if INTERNAL_INDEX_TRUNCATE char *ep; ep = (char *) fs_get((ie->datalen + 1) * sizeof(char)); sprintf(ep, "%.*s", ie->wid, ie->data); /* and other stuff to pack trunc'd element into a new object */ #endif objp = Tcl_NewStringObj(ie->data, ie->datalen); } else objp = Tcl_NewStringObj("", -1); if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK) return(TCL_ERROR); if(ie->color){ Tcl_Obj *objColor; char hexcolor[32]; if((objp = Tcl_NewListObj(0, NULL)) == NULL) return(TCL_ERROR); hex_colorstr(hexcolor, ie->color->fg); objColor = Tcl_NewStringObj(hexcolor, -1); if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK) return(TCL_ERROR); hex_colorstr(hexcolor, ie->color->bg); objColor = Tcl_NewStringObj(hexcolor, -1); if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK) return(TCL_ERROR); } else objp = Tcl_NewStringObj("", -1); if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK) return(TCL_ERROR); /* * IF we ever want to map the thread characters into nice * graphical symbols or take advantage of features like clicking * on a thread element to collapse and such, we need to have * element tagging. That's what the object creation and append * are placeholders for */ switch(ie->type){ case eThreadInfo : objp = Tcl_NewStringObj("threadinfo", -1); break; case eText : objp = NULL; break; default : objp = Tcl_NewStringObj(int2string(ie->type), -1); break; } if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK) return(TCL_ERROR); } if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){ return(TCL_ERROR); } } } return(TCL_OK); } /* * peAppendIndexColor - append index line's foreground/background color * * Params: * */ int peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched) { char hexfg[32], hexbg[32]; ICE_S *h; if((h = build_header_work(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), gPeITop, gPeICount, fetched)) && h->color_lookup_done && h->linecolor){ hex_colorstr(hexfg, h->linecolor->fg); hex_colorstr(hexbg, h->linecolor->bg); return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg)); } return(peAppListF(interp, aObj, "%s", "")); } /* * peMessageStatusBits - return list flags indicating pine status bits * * Params: * * Returns: list of lists where: * * the first element is the list of * field elements data * * the second element is a two element * list containing the lines foreground * and background colors */ int peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { Tcl_SetResult(interp, peMsgStatBitString(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), peMessageNumber(uid), gPeITop, gPeICount, NULL), TCL_STATIC); return(TCL_OK); } char * peMsgStatBitString(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long msgno, long top_msgno, long msgcount, int *fetched) { static char buf[36]; int i; long raw; MESSAGECACHE *mc; ICE_S *h; raw = mn_m2raw(msgmap, msgno); if((h = build_header_work(state, stream, msgmap, msgno, top_msgno, msgcount, fetched)) && (mc = mail_elt(stream, raw))){ /* return a string representing a bit field where: index meaning ----- ------- 0 "New" 1 deleted 2 answered 3 flagged 4 to us 5 cc us 6 recent 7 forwarded 8 attachments */ i = 0; buf[i++] = (mc->seen) ? '0' : '1'; buf[i++] = (mc->deleted) ? '1' : '0'; buf[i++] = (mc->answered) ? '1' : '0'; buf[i++] = (mc->flagged) ? '1' : '0'; buf[i++] = (h->to_us) ? '1' : '0'; buf[i++] = (h->cc_us) ? '1' : '0'; buf[i++] = (mc->recent) ? '1' : '0'; buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0'; buf[i++] = '0'; buf[i++] = '\0'; return(buf); } return("100000000"); } Tcl_Obj * peMsgStatNameList(Tcl_Interp *interp, struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long msgno, long top_msgno, long msgcount, int *fetched) { Tcl_Obj *objList; long raw; MESSAGECACHE *mc; ICE_S *h; objList = Tcl_NewListObj(0, NULL); raw = mn_m2raw(msgmap, msgno); if((h = build_header_work(state, stream, msgmap, msgno, top_msgno, msgcount, fetched)) && (mc = mail_elt(stream, raw))){ if(!mc->seen) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1)); if(mc->deleted) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1)); if(mc->answered) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1)); if(mc->flagged) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1)); if(h->to_us) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1)); if(h->cc_us) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1)); if(mc->recent) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1)); if(user_flag_is_set(stream, raw, FORWARDED_FLAG)) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1)); if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT)) Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1)); } return(objList); } /* * peReplyHeaders - return subject used in reply to given message * * Params: * * Returns: list of header value pairs where headers are: * In-Reply-To:, Subject:, Cc: * */ int peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { long raw; int flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE; char *errmsg = NULL, *fcc = NULL, *sect = NULL; ENVELOPE *env, *outgoing; BODY *body = NULL; ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent; saved_from = (ADDRESS *) NULL; saved_to = (ADDRESS *) NULL; saved_cc = (ADDRESS *) NULL; saved_resent = (ADDRESS *) NULL; raw = peSequenceNumber(uid); /* if we're given a valid section number that * corresponds to a valid msg/rfc822 body part * then set up headers in attached message. */ if(objc == 1 && objv[0] && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0' && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect)) && body->type == TYPEMESSAGE && !strucmp(body->subtype, "rfc822")){ env = body->nested.msg->env; } else{ sect = NULL; env = mail_fetchstructure(ps_global->mail_stream, raw, NULL); } if(env){ if(!reply_harvest(ps_global, raw, sect, env, &saved_from, &saved_to, &saved_cc, &saved_resent, &flags)){ Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_ERROR); } outgoing = mail_newenvelope(); reply_seed(ps_global, outgoing, env, saved_from, saved_to, saved_cc, saved_resent, &fcc, flags, &errmsg); if(errmsg){ if(*errmsg){ q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg); } fs_give((void **)&errmsg); } env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL); outgoing->subject = reply_subject(env->subject, NULL, 0); outgoing->in_reply_to = reply_in_reply_to(env); err = !(peAppListF(interp, Tcl_GetObjResult(interp), "%s%a", "to", outgoing->to) == TCL_OK && peAppListF(interp, Tcl_GetObjResult(interp), "%s%a", "cc", outgoing->cc) == TCL_OK && peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK && peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", "subject", rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, outgoing->subject)) == TCL_OK && (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE)); /* Fill in x-reply-uid data and append it */ if(!err && ps_global->mail_stream->uid_validity){ char *prefix = reply_quote_str(env); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "(%lu %s)(1 %lu %lu)%s", strlen(prefix), prefix, ps_global->mail_stream->uid_validity, uid, ps_global->mail_stream->mailbox); fs_give((void **) &prefix); err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", "x-reply-uid", tmp_20k_buf) != TCL_OK; } mail_free_envelope(&outgoing); if(err) return(TCL_ERROR); } else Tcl_SetResult(interp, "", TCL_VOLATILE); return(TCL_OK); } /* * peReplyText - return subject used in reply to given message * * Params: * * Returns: * */ int peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { long msgno; char *prefix, *sect = NULL; int rv = TCL_OK; ENVELOPE *env; BODY *body = NULL, *orig_body; STORE_S *msgtext; REDRAFT_POS_S *redraft_pos = NULL; Tcl_Obj *objBody = NULL, *objAttach = NULL; msgno = peSequenceNumber(uid); if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){ Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE); return(TCL_ERROR); } /*--- Grab current envelope ---*/ /* if we're given a valid section number that * corresponds to a valid msg/rfc822 body part * then set up to reply the attached message's * text. */ if(objc == 2 && objv[1] && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0' && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect)) && body->type == TYPEMESSAGE && !strucmp(body->subtype, "rfc822")){ env = body->nested.msg->env; orig_body = body->nested.msg->body; } else{ sect = NULL; env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body); if(!(env && orig_body)){ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE); return(TCL_ERROR); } } if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL) prefix = cpystr(prefix); else prefix = reply_quote_str(env); /* * BUG? Should there be some way to signal to reply_bddy * that we'd like it to produced format=flowed body text? * right now it's hardwired to in pine/reply.c */ if((body = reply_body(ps_global->mail_stream, env, orig_body, msgno, sect, msgtext, prefix, TRUE, NULL, TRUE, &redraft_pos)) != NULL){ objBody = Tcl_NewListObj(0, NULL); peSoStrToList(interp, objBody, msgtext); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody); /* sniff for attachments */ objAttach = peMsgAttachCollector(interp, body); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach); pine_free_body(&body); } else{ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE); rv = TCL_ERROR; } fs_give((void **) &prefix); return(rv); } int peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so) { char *sp, *ep; Tcl_Obj *objp; for(ep = (char *) so_text(so); *ep; ep++){ sp = ep; while(*ep && *ep != '\n') ep++; objp = Tcl_NewStringObj(sp, ep - sp); if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK) return(FALSE); } return(TRUE); } /* * peForwardHeaders - return subject used in forward of given message * * Params: * * Returns: * */ int peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { int result; long raw; char *tmp, *sect = NULL; ENVELOPE *env; BODY *body; raw = peSequenceNumber(uid); /* if we're given a valid section number that * corresponds to a valid msg/rfc822 body part * then set up headers in attached message. */ if(objc == 1 && objv[0] && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0' && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect)) && body->type == TYPEMESSAGE && !strucmp(body->subtype, "rfc822")){ env = body->nested.msg->env; } else{ sect = NULL; env = mail_fetchstructure(ps_global->mail_stream, raw, NULL); } if(env){ tmp = forward_subject(env, FS_NONE); result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", "subject", tmp); fs_give((void **) &tmp); /* Fill in x-reply-uid data and append it */ if(result == TCL_OK && ps_global->mail_stream->uid_validity){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s", ps_global->mail_stream->uid_validity, uid, ps_global->mail_stream->mailbox); result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", "x-reply-uid", tmp_20k_buf) != TCL_OK; } return(result); } Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_ERROR); } /* * peForwardText - return body of message used in * forward of given message * * Params: * * Returns: * */ int peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { long msgno; char *bodtext, *p, *sect = NULL; int rv = TCL_OK; ENVELOPE *env; BODY *body, *orig_body; STORE_S *msgtext; Tcl_Obj *objBody = NULL, *objAttach = NULL; msgno = peSequenceNumber(uid); if(objc == 1 && objv[0]) sect = Tcl_GetStringFromObj(objv[0], NULL); if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){ Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE); return(TCL_ERROR); } if(F_ON(F_FORWARD_AS_ATTACHMENT, ps_global)){ PART **pp; long totalsize = 0L; /*---- New Body to start with ----*/ body = mail_newbody(); body->type = TYPEMULTIPART; /*---- The TEXT part/body ----*/ body->nested.part = mail_newbody_part(); body->nested.part->body.type = TYPETEXT; body->nested.part->body.contents.text.data = (unsigned char *) msgtext; pp = &(body->nested.part->next); /*---- The Message body subparts ----*/ env = pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL); if(forward_mime_msg(ps_global->mail_stream, msgno, (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){ totalsize = (*pp)->body.size.bytes; pp = &((*pp)->next); } } else{ /*--- Grab current envelope ---*/ /* if we're given a valid section number that * corresponds to a valid msg/rfc822 body part * then set up to forward the attached message's * text. */ if(sect && *sect != '\0' && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect)) && body->type == TYPEMESSAGE && !strucmp(body->subtype, "rfc822")){ env = body->nested.msg->env; orig_body = body->nested.msg->body; } else{ sect = NULL; env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body); if(!(env && orig_body)){ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE); return(TCL_ERROR); } } body = forward_body(ps_global->mail_stream, env, orig_body, msgno, sect, msgtext, FWD_NONE); } if(body){ bodtext = (char *) so_text(msgtext); objBody = Tcl_NewListObj(0, NULL); for(p = bodtext; *p; p++){ Tcl_Obj *objp; bodtext = p; while(*p && *p != '\n') p++; objp = Tcl_NewStringObj(bodtext, p - bodtext); Tcl_ListObjAppendElement(interp, objBody, objp); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody); /* sniff for attachments */ objAttach = peMsgAttachCollector(interp, body); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach); pine_free_body(&body); } else{ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE); rv = TCL_ERROR; } return(rv); } /* * peDetach - * * Params: argv[0] == attachment part number * argv[1] == directory to hold tmp file * * Returns: list containing: * * 0) response: OK or ERROR * if OK * 1) attachment's mime type * 2) attachment's mime sub-type * 3) attachment's size in bytes (decoded) * 4) attachment's given file name (if any) * 5) tmp file holding raw attachment data */ int peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *part, *err, *tfd, *tfn = NULL, *filename; long raw; gf_io_t pc; BODY *body; STORE_S *store; Tcl_Obj *rvobj, *tObj, *stObj, *fnObj; if((part = Tcl_GetStringFromObj(objv[0], NULL)) && (raw = peSequenceNumber(uid)) && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL); err = NULL; if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){ tfn = temp_nam(tfd = NULL, "pd"); } else if(is_writable_dir(tfd) == 0){ tfn = temp_nam(tfd, "pd"); } else tfn = tfd; filename = Tcl_GetStringFromObj(fnObj, NULL); dprint((5, "PEDetach(name: %s, tmpfile: %s)", filename ? filename : "", tfn)); if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){ gf_set_so_writec(&pc, store); err = detach(ps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0); gf_clear_so_writec(store); so_give(&store); } else err = "Can't allocate internal storage"; } else err = "Can't get message data"; if(err){ if(tfn) unlink(tfn); dprint((1, "PEDetach FAIL: %d: %s", errno, err)); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } /* package up response */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(1, &tObj)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(1, &stObj)); rvobj = Tcl_NewLongObj(name_file_size(tfn)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(1, &rvobj)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(1, &fnObj)); rvobj = Tcl_NewStringObj(tfn, -1); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(1, &rvobj)); return(TCL_OK); } /* * peAttachInfo - * * Params: argv[0] == attachment part number * * Returns: list containing: * * 0) response: OK or ERROR * if OK * 1) attachment's mime type * 2) attachment's mime sub-type * 3) attachment's size in bytes (decoded) * 4) attachment's given file name (if any) * 5) tmp file holding raw attachment data */ int peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *part; long raw; BODY *body; PARMLIST_S *plist; Tcl_Obj *tObj, *stObj, *fnObj; if((part = Tcl_GetStringFromObj(objv[0], NULL)) && (raw = peSequenceNumber(uid)) && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL); } else{ Tcl_SetResult(interp, "Can't get message data", TCL_STATIC); return(TCL_ERROR); } /* package up response */ /* filename */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj); /* type */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj); /* subtype */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj); /* encoding */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj((body->encoding < ENCMAX) ? body_encodings[body->encoding] : "Unknown", -1)); /* parameters */ if((plist = rfc2231_newparmlist(body->parameter)) != NULL){ Tcl_Obj *lObj = Tcl_NewListObj(0, NULL); Tcl_Obj *pObj[2]; while(rfc2231_list_params(plist)){ pObj[0] = Tcl_NewStringObj(plist->attrib, -1); pObj[1] = Tcl_NewStringObj(plist->value, -1); Tcl_ListObjAppendElement(interp, lObj, Tcl_NewListObj(2, pObj)); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj); rfc2231_free_parmlist(&plist); } /* size guesstimate */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(comatose((body->encoding == ENCBASE64) ? ((body->size.bytes * 3)/4) : body->size.bytes), -1)); return(TCL_OK); } /* * peSaveDefault - Default saved file name for the given message * specified collection/folder * * Params: * * Returns: name of saved message folder or empty string * */ int peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *folder; CONTEXT_S *cntxt, *cp; int colid; long rawno; ENVELOPE *env; if(uid){ if(!(env = pine_mail_fetchstructure(ps_global->mail_stream, rawno = peSequenceNumber(uid), NULL))){ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_ERROR); } } else env = NULL; if(!(folder = save_get_default(ps_global, env, rawno, NULL, &cntxt))){ Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE); return(TCL_ERROR); /* message expunged! */ } for(colid = 0, cp = ps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next) ; if(!cp) colid = 0; (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(colid)); (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(folder, -1)); return(TCL_OK); } /* * peSaveWork - Save message with given UID in current folder to * specified collection/folder * * Params: argv[0] == destination context number * argv[1] == testination foldername * * */ int peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags) { int flgs = 0, i, colid; char *folder, *err = NULL; CONTEXT_S *cp; if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){ if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid)); for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next) if(i == colid) break; if(cp){ if(!READONLY_FOLDER(ps_global->mail_stream) && (sflags & PSW_COPY) != PSW_COPY && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, ps_global))) flgs |= SV_DELETE; if(colid == 0 && !strucmp(folder, "inbox")) flgs |= SV_INBOXWOCNTXT; if(sflags & (PSW_COPY | PSW_MOVE)) flgs |= SV_FIX_DELS; i = save(ps_global, ps_global->mail_stream, cp, folder, sp_msgmap(ps_global->mail_stream), flgs); if(i == mn_total_cur(sp_msgmap(ps_global->mail_stream))){ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 1L){ if(ps_global->context_list->next && context_isambig(folder)){ char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders"; snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Message %s %s to \"%.15s%s\" in <%.15s%s>", long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), (sflags & PSW_MOVE) ? "moved" : "copied", folder, (strlen(folder) > 15) ? "..." : "", tag, (strlen(tag) > 15) ? "..." : ""); } else snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Message %s %s to folder \"%.27s%s\"", long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), (sflags & PSW_MOVE) ? "moved" : "copied", folder, (strlen(folder) > 27) ? "..." : ""); } else{ /* with mn_set_cur above, this *should not* happen */ Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE); return(TCL_ERROR); } if(sflags == PSW_NONE && (flgs & SV_DELETE)){ strncat(tmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; } q_status_message(SM_ORDER, 0, 3, tmp_20k_buf); return(TCL_OK); } err = ps_global->last_error; } else err = "open: Unrecognized collection ID"; } else err = "open: Can't read folder"; } else err = "open: Can't get collection ID"; Tcl_SetResult(interp, err, TCL_VOLATILE); return(TCL_ERROR); } /* * peSave - Save message with given UID in current folder to * specified collection/folder * * Params: argv[0] == destination context number * argv[1] == testination foldername * * NOTE: just a wrapper around peSaveWork */ int peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peSaveWork(interp, uid, objc, objv, PSW_NONE)); } /* * peCopy - Copy message with given UID in current folder to * specified collection/folder * * Params: argv[0] == destination context number * argv[1] == testination foldername * * NOTE: just a wrapper around peSaveWork that makes sure * delete-on-save is NOT set */ int peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peSaveWork(interp, uid, objc, objv, PSW_COPY)); } /* * peMove - Move message with given UID in current folder to * specified collection/folder * * Params: argv[0] == destination context number * argv[1] == testination foldername * * NOTE: just a wrapper around peSaveWork that makes sure * delete-on-save IS set so it can be expunged */ int peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { return(peSaveWork(interp, uid, objc, objv, PSW_MOVE)); } /* * peGotoDefault - Default Goto command file name for the given message * specified collection/folder * * Params: * * Returns: name of Goto command default folder or empty string * */ int peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv) { char *folder = NULL; CONTEXT_S *cntxt, *cp; int colid, inbox; cntxt = broach_get_folder(ps_global->context_current, &inbox, &folder); for(colid = 0, cp = ps_global->context_list; cp != cntxt ; colid++, cp = cp->next) ; if(!cp) colid = 0; (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(colid)); (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(folder ? folder : "", -1)); return(TCL_OK); } /* * peReplyQuote - * * Params: argv[0] == attachment part number * * Returns: list containing: * */ int peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *quote; ENVELOPE *env; if(uid){ if((env = pine_mail_fetchstructure(ps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){ quote = reply_quote_str(env); Tcl_SetResult(interp, quote, TCL_VOLATILE); fs_give((void **) "e); } else{ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_ERROR); } } else Tcl_SetResult(interp, "> ", TCL_VOLATILE); return(TCL_OK); } void peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp) { char *ptype = NULL, *psubtype = NULL, *pfile = NULL; /*------- Figure out suggested file name ----*/ if(body){ if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){ /* * if part is generic, see if we can get anything * more from the suggested filename's extension... */ if(body->type == TYPEAPPLICATION && (!body->subtype || !strucmp(body->subtype, "octet-stream"))){ BODY *fakebody = mail_newbody(); if(set_mime_type_by_extension(fakebody, pfile)){ ptype = body_type_names(fakebody->type); psubtype = cpystr(fakebody->subtype); } mail_free_body(&fakebody); } } if(!ptype) { ptype = body_type_names(body->type); psubtype = cpystr(body->subtype ? body->subtype : (body->type == TYPETEXT) ? "plain" : (body->type == TYPEAPPLICATION) ? "octet-stream" : ""); } } else{ ptype = body_type_names(TYPETEXT); psubtype = cpystr("plain"); } if(extObjp){ *extObjp = Tcl_NewStringObj("", 0); if(ptype && psubtype && pfile){ size_t l; char *mtype; char extbuf[32]; /* mailcap.c limits to three */ l = strlen(ptype) + strlen(psubtype) + 1; mtype = (char *) fs_get((l+1) * sizeof(char)); snprintf(mtype, l+1, "%s/%s", ptype, psubtype); if(!set_mime_extension_by_type(extbuf, mtype)){ char *dotp, *p; for(dotp = NULL, p = pfile; *p; p++) if(*p == '.') dotp = p + 1; if(dotp) Tcl_SetStringObj(*extObjp, dotp, -1); } else Tcl_SetStringObj(*extObjp, extbuf, -1); fs_give((void **) &mtype); } } if(tObjp) *tObjp = Tcl_NewStringObj(ptype, -1); if(psubtype){ if(stObjp) *stObjp = Tcl_NewStringObj(psubtype, -1); fs_give((void **) &psubtype); } else if(stObjp) *stObjp = Tcl_NewStringObj("", 0); if(pfile){ if(fnObjp) *fnObjp = Tcl_NewStringObj(pfile, -1); fs_give((void **) &pfile); } else if(fnObjp) *fnObjp = Tcl_NewStringObj("", 0); } /* * peAppListF - generate a list of elements based on fmt string, * then append it to the given list object * */ int peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...) { va_list args; char *p, *sval, nbuf[128]; int ival, err = 0; unsigned int uval; long lval; unsigned long luval; PATTERN_S *pval; ADDRESS *aval; INTVL_S *vval; Tcl_Obj *lObj = NULL, *sObj; if((lObj = Tcl_NewListObj(0, NULL)) != NULL){ va_start(args, fmt); for(p = fmt; *p && !err; p++){ sObj = NULL; if(*p == '%') switch(*++p){ case 'i' : /* int value */ ival = va_arg(args, int); if((sObj = Tcl_NewIntObj(ival)) == NULL) err++; break; case 'u' : /* unsigned int value */ uval = va_arg(args, unsigned int); snprintf(nbuf, sizeof(nbuf), "%u", uval); if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL) err++; break; case 'l' : /* long value */ if(*(p+1) == 'u'){ p++; luval = va_arg(args, unsigned long); snprintf(nbuf, sizeof(nbuf), "%lu", luval); if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL) err++; } else{ lval = va_arg(args, long); if((sObj = Tcl_NewLongObj(lval)) == NULL) err++; } break; case 's' : /* string value */ sval = va_arg(args, char *); sObj = Tcl_NewStringObj(sval ? sval : "", -1); if(sObj == NULL) err++; break; case 'a': /* ADDRESS list */ aval = va_arg(args, ADDRESS *); if(aval){ char *tmp, *p; RFC822BUFFER rbuf; size_t len; len = est_size(aval); tmp = (char *) fs_get(len * sizeof(char)); tmp[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = tmp; rbuf.cur = tmp; rbuf.end = tmp+len-1; rfc822_output_address_list(&rbuf, aval, 0L, NULL); *rbuf.cur = '\0'; p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp); sObj = Tcl_NewStringObj(p, strlen(p)); fs_give((void **) &tmp); } else sObj = Tcl_NewStringObj("", -1); break; case 'p': /* PATTERN_S * */ pval = va_arg(args, PATTERN_S *); sval = pattern_to_string(pval); sObj = Tcl_NewStringObj(sval ? sval : "", -1); break; case 'v': /* INTVL_S * */ vval = va_arg(args, INTVL_S *); if(vval){ for(; vval != NULL; vval = vval->next){ peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax); } } else sObj = Tcl_NewListObj(0, NULL); break; case 'o': /* Tcl_Obj * */ sObj = va_arg(args, Tcl_Obj *); break; } if(sObj) Tcl_ListObjAppendElement(interp, lObj, sObj); } va_end(args); } return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR); } /* * pePatAppendID - append list of pattern identity variables to given object */ void pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat) { Tcl_Obj *resObj; resObj = Tcl_NewListObj(0, NULL); peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick); peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment); Tcl_ListObjAppendElement(interp, patObj, resObj); } /* * pePatAppendPattern - append list of pattern variables to given object */ void pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat) { ARBHDR_S *ah; Tcl_Obj *resObj; resObj = Tcl_NewListObj(0, NULL); peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to); peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from); peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender); peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc); peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip); peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic); peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news); peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj); peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext); peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext); peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword); peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets); peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score); peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age); peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size); if((ah = pat->patgrp->arbhdr) != NULL){ Tcl_Obj *hlObj, *hObj; hlObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1)); for(; ah; ah = ah->next){ hObj = Tcl_NewListObj(0, NULL); peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p); Tcl_ListObjAppendElement(interp, hlObj, hObj); } Tcl_ListObjAppendElement(interp, resObj, hlObj); } switch(pat->patgrp->fldr_type){ case FLDR_ANY: peAppListF(interp, resObj, "%s%s", "ftype", "any"); break; case FLDR_NEWS: peAppListF(interp, resObj, "%s%s", "ftype", "news"); break; case FLDR_EMAIL: peAppListF(interp, resObj, "%s%s", "ftype", "email"); break; case FLDR_SPECIFIC: peAppListF(interp, resObj, "%s%s", "ftype", "specific"); break; } peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder); peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new)); peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec)); peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del)); peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp)); peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans)); peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj)); peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom)); peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy)); Tcl_ListObjAppendElement(interp, patObj, resObj); } char * pePatStatStr(int value) { switch(value){ case PAT_STAT_EITHER: return("either"); break; case PAT_STAT_YES: return("yes"); break; default : return("no"); break; } } /* * peCreateUserContext - create new ps_global and set it up */ char * peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf) { if(ps_global) peDestroyUserContext(&ps_global); set_collation(1, 1); ps_global = new_pine_struct(); /*---------------------------------------------------------------------- Place any necessary constraints on pith processing ----------------------------------------------------------------------*/ /* got thru close procedure without expunging */ ps_global->noexpunge_on_close = 1; /* do NOT let user set path to local executable */ ps_global->vars[V_SENDMAIL_PATH].is_user = 0; /*---------------------------------------------------------------------- Proceed with reading acquiring user settings ----------------------------------------------------------------------*/ if(ps_global->pinerc) fs_give((void **) &ps_global->pinerc); if(ps_global->prc) free_pinerc_s(&ps_global->prc); if(!IS_REMOTE(config)){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config); return(tmp_20k_buf); } ps_global->prc = new_pinerc_s(config); if(defconf){ if(ps_global->pconf) free_pinerc_s(&ps_global->pconf); ps_global->pconf = new_pinerc_s(defconf); } /* * Fake up some user information */ ps_global->ui.login = cpystr(user); #ifdef DEBUG /* * Prep for IMAP debugging */ setup_imap_debug(); #endif /* CHECK FOR AND PASS BACK ANY INIT ERRORS */ return(peLoadConfig(ps_global)); } void peDestroyUserContext(struct pine **pps) { completely_done_with_adrbks(); free_pinerc_strings(pps); #if 0 imap_flush_passwd_cache(TRUE); #endif clear_index_cache(sp_inbox_stream(), 0); free_newsgrp_cache(); mailcap_free(); close_patterns(0L); free_extra_hdrs(); free_contexts(&ps_global->context_list); pico_endcolor(); free_strlist(&peCertHosts); free_pine_struct(pps); } char * peLoadConfig(struct pine *pine_state) { int rv; char *s, *db = NULL; extern void init_signals(void); /* in signal.c */ if(!pine_state) return("No global state present"); #if 0 /*turned off because we don't care about local user*/ /* need home directory early */ get_user_info(&pine_state->ui); pine_state->home_dir = cpystr((getenv("HOME") != NULL) ? getenv("HOME") : pine_state->ui.homedir); #endif init_pinerc(pine_state, &db); fs_give((void **) &db); /* * Initial allocation of array of stream pool pointers. * We do this before init_vars so that we can re-use streams used for * remote config files. These sizes may get changed later. */ ps_global->s_pool.max_remstream = 2; ps_global->c_client_error[0] = '\0'; pePrepareForAuthException(); peInitVars(pine_state); if((s = peAuthException()) != NULL) return(s); else if(ps_global->c_client_error[0]) return(ps_global->c_client_error); mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened); mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback); mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt); /* * Install callback to handle certificate validation failures, * allowing the user to continue if they wish. */ mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery); mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure); /* * Set up a c-client read timeout and timeout handler. In general, * it shouldn't happen, but a server crash or dead link can cause * pine to appear wedged if we don't set this up... */ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)((pine_state->VAR_TCPOPENTIMEO && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4) ? (long) rv : 30L)); mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout); if(pine_state->VAR_RSHOPENTIMEO && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4)) mail_parameters(NULL, SET_RSHTIMEOUT, (void *) (long) rv); if(pine_state->VAR_SSHOPENTIMEO && ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4)) mail_parameters(NULL, SET_SSHTIMEOUT, (void *) (long) rv); /* * Tell c-client not to be so aggressive about uid mappings */ mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20); /* * Setup referral handling */ mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral); mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy); /* * Install extra headers to fetch along with all the other stuff * mail_fetch_structure and mail_fetch_overview requests. */ calc_extra_hdrs(); if(get_extra_hdrs()) (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs()); (void) init_username(pine_state); (void) init_hostname(ps_global); #ifdef ENABLE_LDAP (void) init_ldap_pname(ps_global); #endif /* ENABLE_LDAP */ if(ps_global->prc && ps_global->prc->type == Loc && can_access(ps_global->pinerc, ACCESS_EXISTS) == 0 && can_access(ps_global->pinerc, EDIT_ACCESS) != 0) ps_global->readonly_pinerc = 1; /* * c-client needs USR2 and we might as well * do something sensible with HUP and TERM */ init_signals(); strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name)); pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0'; init_folders(pine_state); /* digest folder spec's */ /* * Various options we want to make sure are set OUR way */ F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state); F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state); F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state); /* * Fake screen dimensions for index formatting and * message display wrap... */ ps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo)); ps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH; ps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH; if(ps_global->VAR_WP_COLUMNS){ int w = atoi(ps_global->VAR_WP_COLUMNS); if(w >= 20 && w <= 128) ps_global->ttyo->screen_cols = w; } ps_global->ttyo->header_rows = 0; ps_global->ttyo->footer_rows = 0; /* init colors */ if(ps_global->VAR_NORM_FORE_COLOR) pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR); if(ps_global->VAR_NORM_BACK_COLOR) pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR); if(ps_global->VAR_REV_FORE_COLOR) pico_rfcolor(ps_global->VAR_REV_FORE_COLOR); if(ps_global->VAR_REV_BACK_COLOR) pico_rbcolor(ps_global->VAR_REV_BACK_COLOR); pico_set_normal_color(); return(NULL); } int peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox) { unsigned long flgs = 0L; char *s; ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); if(do_inbox) flgs |= DB_INBOXWOCNTXT; if(do_broach_folder(mailbox, context, NULL, flgs) && ps_global->mail_stream){ dprint((SYSDBG_INFO, "Mailbox open: %s", ps_global->mail_stream->mailbox ? ps_global->mail_stream->mailbox : "")); return(TCL_OK); } Tcl_SetResult(interp, (s = peAuthException()) ? s : (*ps_global->last_error) ? ps_global->last_error : "Login Error", TCL_VOLATILE); return(TCL_ERROR); } void peDestroyStream(struct pine *ps) { int cur_is_inbox; if(ps){ cur_is_inbox = (sp_inbox_stream() == ps_global->mail_stream); /* clean up open streams */ if(ps->mail_stream){ expunge_and_close(ps->mail_stream, NULL, EC_NONE); ps_global->mail_stream = NULL; ps_global->cur_folder[0] = '\0'; } if(ps->msgmap) mn_give(&ps->msgmap); if(sp_inbox_stream() && !cur_is_inbox){ ps->mail_stream = sp_inbox_stream(); ps->msgmap = sp_msgmap(ps->mail_stream); sp_set_expunge_count(ps_global->mail_stream, 0L); expunge_and_close(sp_inbox_stream(), NULL, EC_NONE); mn_give(&ps->msgmap); } } } /* * pePrepareForAuthException - set globals to get feedback from bowels of c-client */ void pePrepareForAuthException(void) { peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0; } /* * pePrepareForAuthException - check globals getting feedback from bowels of c-client */ char * peAuthException() { static char buf[CRED_REQ_SIZE]; if(peCertQuery){ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor); return(buf); } if(peCertFailure){ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor); return(buf); } if(peNoPassword){ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor); return(buf); } if(peCredentialError){ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor); return(buf); } return(NULL); } void peInitVars(struct pine *ps) { init_vars(ps, NULL); /* * fix display/keyboard-character-set to utf-8 * * */ if(ps->display_charmap) fs_give((void **) &ps->display_charmap); ps->display_charmap = cpystr(WP_INTERNAL_CHARSET); if(ps->keyboard_charmap) fs_give((void **) &ps->keyboard_charmap); ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET); (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);; } int peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1]; long rawno; ENVELOPE *env, *outgoing = NULL; METAENV *metaenv; PINEFIELD *custom; BODY *body = NULL; if(uid){ rawno = peSequenceNumber(uid); if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){ if(objc == 2 && objv[1]){ subj = Tcl_GetStringFromObj(objv[1], NULL); } else if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) != NULL){ subj = env->subject; } else{ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE); return(TCL_ERROR); } if((errstr = bounce_msg_body(ps_global->mail_stream, rawno, NULL, &to, subj, &outgoing, &body, NULL))){ Tcl_SetResult(interp, errstr, TCL_VOLATILE); return(TCL_ERROR); } metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs()); if(!outgoing->from) outgoing->from = generate_from(); rfc822_date(tmp_20k_buf); outgoing->date = (unsigned char *) cpystr(tmp_20k_buf); outgoing->return_path = rfc822_cpy_adr(outgoing->from); if(!outgoing->message_id) outgoing->message_id = generate_message_id(); /* NO FCC */ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK) errstr = errbuf; pine_free_body(&body); pine_free_env(&metaenv); if(custom) free_customs(custom); mail_free_envelope(&outgoing); pine_free_body(&body); } } Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE); return(errstr ? TCL_ERROR : TCL_OK); } int peMessageSpamNotice(interp, uid, objc, objv) Tcl_Interp *interp; imapuid_t uid; int objc; Tcl_Obj **objv; { char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL; long rawno; if(uid){ rawno = peSequenceNumber(uid); if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){ if(objv[1]) subj = Tcl_GetStringFromObj(objv[1], NULL); rs = peSendSpamReport(rawno, to, subj, errbuf); } } Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE); return(rs ? TCL_ERROR : TCL_OK); } char * peSendSpamReport(long rawno, char *to, char *subj, char *errbuf) { char *errstr = NULL, *tmp_a_string; ENVELOPE *env, *outgoing; METAENV *metaenv; PINEFIELD *custom; BODY *body; static char *fakedomain = "@"; void *msgtext; if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) == NULL){ return(ps_global->last_error); } /* empty subject gets "spam" subject */ if(!(subj && *subj)) subj = env->subject; /*---- New Body to start with ----*/ body = mail_newbody(); body->type = TYPEMULTIPART; /*---- The TEXT part/body ----*/ body->nested.part = mail_newbody_part(); body->nested.part->body.type = TYPETEXT; if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){ pine_free_body(&body); return("peSendSpamReport: Can't allocate text"); } else{ sprintf(tmp_20k_buf, "The attached message is being reported to <%s> as Spam\n", to); so_puts((STORE_S *) msgtext, tmp_20k_buf); body->nested.part->body.contents.text.data = msgtext; } /*---- Attach the raw message ----*/ if(forward_mime_msg(ps_global->mail_stream, rawno, NULL, env, &(body->nested.part->next), msgtext)){ outgoing = mail_newenvelope(); metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs()); } else{ pine_free_body(&body); return("peSendSpamReport: Can't generate forwarded message"); } /* rfc822_parse_adrlist feels free to destroy input so copy */ tmp_a_string = cpystr(to); rfc822_parse_adrlist(&outgoing->to, tmp_a_string, (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) ? fakedomain : ps_global->maildomain); fs_give((void **) &tmp_a_string); outgoing->from = generate_from(); outgoing->subject = cpystr(subj); outgoing->return_path = rfc822_cpy_adr(outgoing->from); outgoing->message_id = generate_message_id(); rfc822_date(tmp_20k_buf); outgoing->date = (unsigned char *) cpystr(tmp_20k_buf); /* NO FCC for Spam Reporting */ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK) errstr = errbuf; pine_free_body(&body); pine_free_env(&metaenv); if(custom) free_customs(custom); mail_free_envelope(&outgoing); pine_free_body(&body); return(errstr); } /* * * * * * * * * * * * * Start of Composer Routines * * * * * * * * * * * */ /* * PEComposeCmd - export various bits of alpine state */ int PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err = "PECompose: Unknown request"; dprint((2, "PEComposeCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else if(!ps_global){ Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC); return(TCL_ERROR); } else{ char *s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(!strcmp(s1, "post")){ long flags = PMC_NONE; if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) flags |= PMC_FORCE_QUAL; return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags)); } else if(objc == 2){ if(!strcmp(s1, "userhdrs")){ int i; char *p; PINEFIELD *custom, *cp; ADDRESS *from; static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL}; custom = peCustomHdrs(); for(i = 0; standard[i]; i++){ p = NULL; for(cp = custom; cp; cp = cp->next) if(!strucmp(cp->name, standard[i])) p = cp->textbuf; peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p); } for(cp = custom; cp != NULL; cp = cp->next){ if(!strucmp(cp->name, "from")){ if(F_OFF(F_ALLOW_CHANGING_FROM, ps_global)) continue; if(cp->textbuf && strlen(cp->textbuf)){ p = cp->textbuf; } else{ RFC822BUFFER rbuf; tmp_20k_buf[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = tmp_20k_buf; rbuf.cur = tmp_20k_buf; rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1; rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL); *rbuf.cur = '\0'; mail_free_address(&from); p = tmp_20k_buf; } } else{ p = cp->textbuf; for(i = 0; standard[i]; i++) if(!strucmp(standard[i], cp->name)) p = NULL; if(!p) continue; } peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p); } if(custom) free_customs(custom); return(TCL_OK); } else if(!strcmp(s1, "syshdrs")){ int i; static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL}; for(i = 0; extras[i]; i++) peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL); return(TCL_OK); } else if(!strcmp(s1, "composehdrs")){ char **p, *q; if((p = ps_global->VAR_COMP_HDRS) && *p){ for(; *p; p++) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(*p, (q = strchr(*p, ':')) ? (q - *p) : -1)); } else Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "fccdefault")){ int ci = 0; CONTEXT_S *c = default_save_context(ps_global->context_list), *c2; for(c2 = ps_global->context_list; c && c != c2; c2 = c2->next) ci++; Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(ci)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->VAR_DEFAULT_FCC ? ps_global->VAR_DEFAULT_FCC : "", -1)); return(TCL_OK); } else if(!strcmp(s1, "noattach")){ peFreeAttach(&peCompAttach); Tcl_SetResult(interp, "OK", TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "from")){ RFC822BUFFER rbuf; ADDRESS *from = generate_from(); tmp_20k_buf[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = tmp_20k_buf; rbuf.cur = tmp_20k_buf; rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1; rfc822_output_address_list(&rbuf, from, 0L, NULL); *rbuf.cur = '\0'; mail_free_address(&from); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_OK); } else if(!strcmp(s1, "attachments")){ COMPATT_S *p; Tcl_Obj *objAttach; for(p = peCompAttach; p; p = p->next) if(p->file){ objAttach = Tcl_NewListObj(0, NULL); /* id */ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1)); /* file name */ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1)); /* file size */ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size)); /* type/subtype */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype); Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf,-1)); /* append to list */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach); } else if(p->body){ char *name; objAttach = Tcl_NewListObj(0, NULL); /* id */ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1)); /* file name */ if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1)); fs_give((void **) &name); } else Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1)); /* file size */ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64) ? ((p->l.b.body->size.bytes * 3)/4) : p->l.b.body->size.bytes)); /* type/subtype */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", body_type_names(p->l.b.body->type), p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown"); Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf, -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach); } return(TCL_OK); } } else if(objc == 3){ if(!strcmp(s1, "unattach")){ char *id; if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(peClearAttachID(id)){ Tcl_SetResult(interp, "OK", TCL_STATIC); return(TCL_OK); } else err = "Can't access attachment id"; } else err = "Can't read attachment id"; } else if(!strcmp(s1, "attachinfo")){ COMPATT_S *a; char *id, *s; /* return: remote-filename size "type/subtype" */ if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if((a = peGetAttachID(id)) != NULL){ if(a->file){ /* file name */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1)); /* file size */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size)); /* type/subtype */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf,-1)); /* description */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1)); return(TCL_OK); } else if(a->body){ char *name; /* file name */ if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1)); fs_give((void **) &name); } else Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1)); /* file size */ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64) ? ((a->l.b.body->size.bytes * 3)/4) : a->l.b.body->size.bytes)); /* type/subtype */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", body_type_names(a->l.b.body->type), a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown"); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1)); /* description */ if(a->l.b.body->description){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description); } else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s); fs_give((void **) &s); } else tmp_20k_buf[0] = '\0'; Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1)); return(TCL_OK); } err = "Unknown attachment type"; } else err = "Can't access attachment id"; } else err = "Can't read attachment id"; } } else if(objc == 7){ if(!strcmp(s1, "attach")){ char *file, *remote, *type, *subtype, *desc; if((file = Tcl_GetStringFromObj(objv[2], NULL)) && (type = Tcl_GetStringFromObj(objv[3], NULL)) && (subtype = Tcl_GetStringFromObj(objv[4], NULL)) && (remote = Tcl_GetStringFromObj(objv[5], NULL))){ int dl; desc = Tcl_GetStringFromObj(objv[6], &dl); if(desc){ Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE); return(TCL_OK); } else err = "Can't read file description"; } else err = "Can't read file name"; } } } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } COMPATT_S * peNewAttach(void) { COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S)); memset(p, 0, sizeof(COMPATT_S)); return(p); } void peFreeAttach(COMPATT_S **a) { if(a && *a){ fs_give((void **) &(*a)->id); if((*a)->file){ if((*a)->l.f.type) fs_give((void **) &(*a)->l.f.type); if((*a)->l.f.subtype) fs_give((void **) &(*a)->l.f.subtype); if((*a)->l.f.remote) fs_give((void **) &(*a)->l.f.remote); if((*a)->l.f.local){ (void) unlink((*a)->l.f.local); fs_give((void **) &(*a)->l.f.local); } if((*a)->l.f.description) fs_give((void **) &(*a)->l.f.description); } else if((*a)->body){ pine_free_body(&(*a)->l.b.body); } peFreeAttach(&(*a)->next); fs_give((void **) a); } } char * peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl) { COMPATT_S *ap = peNewAttach(), *p; long hval; ap->file = TRUE; ap->l.f.local = cpystr(f); ap->l.f.size = name_file_size(f); hval = line_hash(f); while(1) /* collisions? */ if(peGetAttachID(ap->id = cpystr(long2string(hval)))){ fs_give((void **) &ap->id); hval += 1; } else break; ap->l.f.remote = cpystr(r ? r : ""); ap->l.f.type = cpystr(t ? t : "Text"); ap->l.f.subtype = cpystr(st ? st : "Plain"); ap->l.f.description = fs_get(dl + 1); snprintf(ap->l.f.description, dl + 1, "%s", d); if((p = peCompAttach) != NULL){ do if(!p->next){ p->next = ap; break; } while((p = p->next) != NULL); } else peCompAttach = ap; return(ap->id); } char * peBodyAttachID(BODY *b) { COMPATT_S *ap = peNewAttach(), *p; unsigned long hval; ap->body = TRUE; ap->l.b.body = copy_body(NULL, b); hval = b->id ? line_hash(b->id) : time(0); while(1) /* collisions? */ if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){ fs_give((void **) &ap->id); hval += 1; } else break; /* move contents pointer to copy */ peBodyMoveContents(b, ap->l.b.body); if((p = peCompAttach) != NULL){ do if(!p->next){ p->next = ap; break; } while((p = p->next) != NULL); } else peCompAttach = ap; return(ap->id); } void peBodyMoveContents(BODY *bs, BODY *bd) { if(bs && bd){ if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){ PART *ps = bs->nested.part, *pd = bd->nested.part; do /* for each part */ peBodyMoveContents(&ps->body, &pd->body); while ((ps = ps->next) && (pd = pd->next)); /* until done */ } else if(bs->contents.text.data){ bd->contents.text.data = bs->contents.text.data; bs->contents.text.data = NULL; } } } COMPATT_S * peGetAttachID(char *h) { COMPATT_S *p; for(p = peCompAttach; p; p = p->next) if(!strcmp(p->id, h)) return(p); return(NULL); } int peClearAttachID(char *h) { COMPATT_S *pp, *pt = NULL; for(pp = peCompAttach; pp; pp = pp->next){ if(!strcmp(pp->id, h)){ if(pt) pt->next = pp->next; else peCompAttach = pp->next; pp->next = NULL; peFreeAttach(&pp); return(TRUE); } pt = pp; } return(FALSE); } /* * peDoPost - handle preparing header and body text for posting, prepare * for any Fcc, then call the mailer to send it out */ int peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp) { int rv = TCL_OK, recipients; char *s; if(commence_fcc(fcc, fcc_cntxtp, TRUE)){ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0'; pePrepareForAuthException(); if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc)) && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){ if((s = peAuthException()) != NULL){ strcpy(errp, s); } else if(ps_global->last_error[0]){ sprintf(errp, "Send Error: %.*s", 64, ps_global->last_error); } else if(ps_global->c_client_error[0]){ sprintf(errp, "Send Error: %.*s", 64, ps_global->c_client_error); } else strcpy(errp, "Sending Failure"); rv = TCL_ERROR; dprint((1, "call_mailer failed!")); } else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){ strcpy(errp, "Fcc Failed!. No message saved."); rv = TCL_ERROR; dprint((1, "explicit fcc write failed!")); } else{ PINEFIELD *pf; REPLY_S *reply = NULL; /* success, now look for x-reply-uid to flip answered flag for? */ for(pf = metaenv->local; pf && pf->name; pf = pf->next) if(!strucmp(pf->name, "x-reply-uid")){ if(pf->textbuf){ if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){ update_answered_flags(reply); if(reply->mailbox) fs_give((void **) &reply->mailbox); if(reply->prefix) fs_give((void **) &reply->prefix); if(reply->data.uid.msgs) fs_give((void **) &reply->data.uid.msgs); fs_give((void **) &reply); } } break; } } } else{ dprint((1,"can't open fcc, cont")); strcpy(errp, "Can't open Fcc"); rv = TCL_ERROR; } return(rv); } /* * pePostponeCmd - export various bits of alpine state */ int PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err = "PEPostpone: unknown request"; long uidl; imapuid_t uid; dprint((2, "PEPostponeCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else if(!ps_global){ Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC); return(TCL_ERROR); } else{ char *s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(!strcmp(s1, "extract")){ if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){ Tcl_Obj *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL; MAILSTREAM *stream; BODY *b; ENVELOPE *env = NULL; PINEFIELD *custom = NULL, *cp; REPLY_S *reply = NULL; ACTION_S *role = NULL; STORE_S *so; long n; int rv = TCL_OK; char *fcc = NULL, *lcc = NULL; unsigned flags = REDRAFT_DEL | REDRAFT_PPND; uid = uidl; if(objc > 3){ /* optional flags */ int i, nFlags; Tcl_Obj **objFlags; char *flagstr; Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags); for(i = 0; i < nFlags; i++){ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){ rv = TCL_ERROR; } if(!strucmp(flagstr, "html")) flags |= REDRAFT_HTML; } } /* BUG: should probably complain if argc > 4 */ if(rv == TCL_OK && postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ if((n = mail_msgno(stream, uid)) > 0L){ if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply, NULL, &custom, &role, /* should role be NULL? */ flags, so)){ char *charset = NULL; /* prepare to package up for caller */ objHdr = Tcl_NewListObj(0, NULL); /* determine body part's charset */ if((charset = parameter_val(b->parameter,"charset")) != NULL){ objOpts = Tcl_NewListObj(0, NULL); peAppListF(interp, objOpts, "%s%s", "charset", charset); fs_give((void **) &charset); } /* body part's MIME subtype */ if(b->subtype && strucmp(b->subtype,"plain")){ if(!objOpts) objOpts = Tcl_NewListObj(0, NULL); peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype); } peAppListF(interp, objHdr, "%s%a", "from", role && role->from ? role->from : env->from); peAppListF(interp, objHdr, "%s%a", "to", env->to); peAppListF(interp, objHdr, "%s%a", "cc", env->cc); peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc); peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to); peAppListF(interp, objHdr, "%s%s", "subject", rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, env->subject)); if(fcc) peFccAppend(interp, objHdr, fcc, -1); for(cp = custom; cp && cp->name; cp = cp->next) switch(cp->type){ case Address : strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; peAppListF(interp, objHdr, "%s%a", lcase((unsigned char *) tmp_20k_buf), *cp->addr); break; case Attachment : break; case Fcc : case Subject : break; /* ignored */ default : strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; peAppListF(interp, objHdr, "%s%s", lcase((unsigned char *) tmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val); break; } if(reply){ /* blat x-Reply-UID: for possible use? */ if(reply->uid){ char uidbuf[MAILTMPLEN], *p; long i; for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){ if(i) sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf)); sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-tmp_20k_buf)); } tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s", reply->prefix ? int2string(strlen(reply->prefix)) : (reply->forwarded) ? "" : "0 ", reply->prefix ? " " : "", reply->prefix ? reply->prefix : "", i, reply->data.uid.validity, tmp_20k_buf, reply->mailbox); peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf); } fs_give((void **) &reply->mailbox); fs_give((void **) &reply->prefix); fs_give((void **) &reply->data.uid.msgs); fs_give((void **) &reply); } objBod = Tcl_NewListObj(0, NULL); peSoStrToList(interp, objBod, so); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod); objAttach = peMsgAttachCollector(interp, b); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach); if(objOpts){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts); } /* clean up */ if(fcc) fs_give((void **) &fcc); if(lcc) fs_give((void **) &lcc); mail_free_envelope(&env); pine_free_body(&b); free_action(&role); /* if Drafts got whacked, open INBOX */ if(!ps_global->mail_stream) do_broach_folder(ps_global->inbox_name, ps_global->context_list, NULL, DB_INBOXWOCNTXT); return(TCL_OK); } so_give(&so); } else err = "Unknown UID"; } else err = "No internal storage"; /* redraft_work cleaned up the "stream" */ } else err = "No Postponed stream"; } else err = "Malformed extract request"; } else if(objc == 2){ if(!strcmp(s1, "any")){ MAILSTREAM *stream; if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){ Tcl_SetResult(interp, "1", TCL_STATIC); if(stream != ps_global->mail_stream) pine_mail_close(stream); } else Tcl_SetResult(interp, "0", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "count")){ MAILSTREAM *stream; if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){ Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC); if(stream != ps_global->mail_stream) pine_mail_close(stream); } else Tcl_SetResult(interp, "-1", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "list")){ MAILSTREAM *stream; ENVELOPE *env; Tcl_Obj *objEnv = NULL, *objEnvList; long n; if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){ if(!stream->nmsgs){ (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND); Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } objEnvList = Tcl_NewListObj(0, NULL); for(n = 1; n <= stream->nmsgs; n++){ if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){ objEnv = Tcl_NewListObj(0, NULL); peAppListF(interp, objEnv, "%s%s", "uid", ulong2string(mail_uid(stream, n))); peAppListF(interp, objEnv, "%s%a", "to", env->to); date_str((char *)env->date, iSDate, 1, tmp_20k_buf, SIZEOF_20KBUF, 0); peAppListF(interp, objEnv, "%s%s", "date", tmp_20k_buf); peAppListF(interp, objEnv, "%s%s", "subj", rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, env->subject)); Tcl_ListObjAppendElement(interp, objEnvList, objEnv); } } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("utf-8", -1)); if(stream != ps_global->mail_stream) pine_mail_close(stream); } return(TCL_OK); } } else if(objc == 3){ if(!strcmp(s1, "append")){ int rv; if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_NONE)) == TCL_OK) Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE); return(rv); } else if(!strcmp(s1, "draft")){ int rv; if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_PRSRV_ATT)) == TCL_OK) Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE); return(rv); } else if(!strcmp(s1, "delete")){ if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){ MAILSTREAM *stream; long rawno; uid = uidl; if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){ if((rawno = mail_msgno(stream, uid)) > 0L){ mail_flag(stream, long2string(rawno), "\\DELETED", ST_SET); ps_global->expunge_in_progress = 1; mail_expunge(stream); ps_global->expunge_in_progress = 0; if(stream != ps_global->mail_stream) pine_mail_actually_close(stream); return(TCL_OK); } else err = "PEPostpone delete: UID no longer exists"; } else err = "PEPostpone delete: No Postponed stream"; } else err = "PEPostpone delete: No uid provided"; } } } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * peDoPostpone - handle postponing after message collection */ int peDoPostpone(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp) { PINEFIELD *pf; int rv; appenduid_t *au; /* * resolve fcc and store it in fcc custom header field data */ if(fcc && *fcc && fcc_cntxtp && *fcc_cntxtp) for(pf = metaenv->local; pf && pf->name; pf = pf->next) if(!strucmp("fcc", pf->name)){ char *name, *rs, path_in_context[MAILTMPLEN]; if(pf->textbuf) /* free old value */ fs_give((void **) &pf->textbuf); /* replace nickname with full name */ if(!(name = folder_is_nick(fcc, FOLDERS(*fcc_cntxtp), FN_NONE))) name = fcc; if(context_isambig(name) && !(((*fcc_cntxtp)->use) & CNTXT_SAVEDFLT)){ context_apply(path_in_context, *fcc_cntxtp, name, sizeof(path_in_context)); rs = IS_REMOTE(path_in_context) ? path_in_context : NULL; } else rs = cpystr(name); if(rs){ pf->textbuf = cpystr(rs); pf->text = &pf->textbuf; } break; } au = mail_parameters(NIL, GET_APPENDUID, NIL); mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb); rv = write_postponed(metaenv, body); mail_parameters(NIL, SET_APPENDUID, (void *) au); return((rv < 0) ? TCL_ERROR : TCL_OK); } /* * peMsgCollector - Collect message parts and call specified handler */ int peMsgCollector(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long flags) { Tcl_Obj **objMsg, **objField, **objBody; int i, j, vl, nMsg, nField, nBody; char *field, *value, *err = NULL; MSG_COL_S md; PINEFIELD *pf; STRLIST_S *tp, *lp; static char *fakedomain = "@"; memset(&md, 0, sizeof(MSG_COL_S)); md.postop_fcc_no_attach = -1; md.postfunc = postfunc; md.qualified_addrs = ((flags & PMC_FORCE_QUAL) == PMC_FORCE_QUAL); if(objc != 1){ Tcl_SetResult(interp, "Malformed message data", TCL_STATIC); return(TCL_ERROR); } else if(!ps_global){ Tcl_SetResult(interp, "No open folder", TCL_STATIC); return(TCL_ERROR); } md.outgoing = mail_newenvelope(); md.metaenv = pine_new_env(md.outgoing, NULL, NULL, md.custom = peCustomHdrs()); Tcl_ListObjGetElements(interp, objv[0], &nMsg, &objMsg); for(i = 0; i < nMsg; i++){ if(Tcl_ListObjGetElements(interp, objMsg[i], &nField, &objField) != TCL_OK){ err = ""; /* interp's result object has error message */ return(peMsgCollected(interp, &md, err, flags)); } if(nField && (field = Tcl_GetStringFromObj(objField[0], NULL))){ if(!strcmp(field, "body")){ if(md.msgtext){ err = "Too many bodies"; return(peMsgCollected(interp, &md, err, flags)); } else if((md.msgtext = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ /* mark storage object as user edited */ (void) so_attr(md.msgtext, "edited", "1"); Tcl_ListObjGetElements(interp, objField[1], &nBody, &objBody); for(j = 0; j < nBody; j++){ value = Tcl_GetStringFromObj(objBody[j], &vl); if(value){ so_nputs(md.msgtext, value, vl); so_puts(md.msgtext, "\n"); } else{ err = "Value read failure"; return(peMsgCollected(interp, &md, err, flags)); } } } else { err = "Can't acquire body storage"; return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(field, "attach")){ char *id; COMPATT_S *a; if(nField == 2 && (id = Tcl_GetStringFromObj(objField[1], NULL)) && (a = peGetAttachID(id))){ tp = new_strlist(id); if((lp = md.attach) != NULL){ do if(!lp->next){ lp->next = tp; break; } while((lp = lp->next) != NULL); } else md.attach = tp; } else{ strcpy(err = tmp_20k_buf, "Unknown attachment ID"); return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(field, "fcc")){ Tcl_Obj **objFcc; int nFcc; if(Tcl_ListObjGetElements(interp, objField[1], &nFcc, &objFcc) == TCL_OK && nFcc == 2 && Tcl_GetIntFromObj(interp, objFcc[0], &md.fcc_colid) == TCL_OK && (value = Tcl_GetStringFromObj(objFcc[1], NULL))){ if(md.fcc) fs_give((void **) &md.fcc); md.fcc = cpystr(value); } else { strcpy(err = tmp_20k_buf, "Unrecognized Fcc specification"); return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(field, "postoption")){ Tcl_Obj **objPO; int nPO, ival; value = NULL; if(Tcl_ListObjGetElements(interp, objField[1], &nPO, &objPO) == TCL_OK && nPO == 2 && (value = Tcl_GetStringFromObj(objPO[0], NULL))){ if(!strucmp(value,"fcc-without-attachments")){ if(Tcl_GetIntFromObj(interp, objPO[1], &ival) == TCL_OK){ md.postop_fcc_no_attach = (ival != 0); } else{ sprintf(err = tmp_20k_buf, "Malformed Post Option: fcc-without-attachments"); return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(value, "charset")){ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){ char *p; for(p = value; ; p++){ /* sanity check */ if(!*p){ md.charset = cpystr(value); break; } if(isspace((unsigned char ) *p) || !isprint((unsigned char) *p)) break; if(p - value > 255) break; } } else{ err = "Post option read failure"; return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(value, "flowed")){ if(F_OFF(F_QUELL_FLOWED_TEXT,ps_global)){ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){ if(!strucmp(value, "yes")) md.flowed = 1; } else{ err = "Post option read failure"; return(peMsgCollected(interp, &md, err, flags)); } } } else if(!strucmp(value, "subtype")){ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){ if(!strucmp(value, "html")) md.html = 1; } else{ err = "Post option read failure"; return(peMsgCollected(interp, &md, err, flags)); } } else if(!strucmp(value, "priority")){ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){ char *priority = NULL; if(!strucmp(value, "highest")) priority = "Highest"; else if(!strucmp(value, "high")) priority = "High"; else if(!strucmp(value, "normal")) priority = "Normal"; else if(!strucmp(value, "low")) priority = "Low"; else if(!strucmp(value, "lowest")) priority = "Lowest"; if(priority){ if((pf = set_priority_header(md.metaenv, priority)) != NULL) pf->text = &pf->textbuf; } } else{ err = "Post option read failure"; return(peMsgCollected(interp, &md, err, flags)); } } else{ sprintf(err = tmp_20k_buf, "Unknown Post Option: %s", value); return(peMsgCollected(interp, &md, err, flags)); } } else{ sprintf(err = tmp_20k_buf, "Malformed Post Option"); return(peMsgCollected(interp, &md, err, flags)); } } else { if(nField != 2){ sprintf(err = tmp_20k_buf, "Malformed header (%s)", field); return(peMsgCollected(interp, &md, err, flags)); } if((value = Tcl_GetStringFromObj(objField[1], &vl)) != NULL){ ADDRESS **addrp = NULL; char **valp = NULL, *valcpy; if(!strucmp(field, "from")){ addrp = &md.outgoing->from; } else if(!strucmp(field, "reply-to")){ addrp = &md.outgoing->reply_to; } else if(!strucmp(field, "to")){ addrp = &md.outgoing->to; } else if(!strucmp(field, "cc")){ addrp = &md.outgoing->cc; } else if(!strucmp(field, "bcc")){ addrp = &md.outgoing->bcc; } else if(!strucmp(field, "subject")){ valp = &md.outgoing->subject; } else if(!strucmp(field, "in-reply-to")){ valp = &md.outgoing->in_reply_to; } else if(!strucmp(field, "newsgroups")){ valp = &md.outgoing->newsgroups; } else if(!strucmp(field, "followup-to")){ valp = &md.outgoing->followup_to; } else if(!strucmp(field, "references")){ valp = &md.outgoing->references; } else if(!strucmp(field, "x-reply-uid")){ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next) if(!strucmp(pf->name, "x-reply-uid")){ valp = pf->text = &pf->textbuf; break; } } else if(!strucmp(field, "x-auth-received")){ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next) if(!strucmp(pf->name, "x-auth-received")){ valp = pf->text = &pf->textbuf; break; } } else{ for(pf = md.metaenv->custom; pf && pf->name; pf = pf->next) if(!strucmp(field, pf->name)){ if(pf->type == Address) addrp = pf->addr; else if(vl) valp = &pf->textbuf; else if(pf->textbuf) fs_give((void **) &pf->textbuf); break; } if(!pf) dprint((2, "\nPOST: unrecognized field - %s\n", field)); } if(valp){ if(*valp) fs_give((void **) valp); sprintf(*valp = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value); } if(addrp){ sprintf(valcpy = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value); for(; *addrp; addrp = &(*addrp)->next) ; rfc822_parse_adrlist(addrp, valcpy, (flags & PMC_FORCE_QUAL) ? fakedomain : ps_global->maildomain); fs_give((void **) &valcpy); } } else{ err = "Value read failure"; return(peMsgCollected(interp, &md, err, flags)); } } } } return(peMsgCollected(interp, &md, err, flags)); } /* * peMsgCollected - Dispatch collected message data and cleanup */ int peMsgCollected(Tcl_Interp *interp, MSG_COL_S *md, char *err, long flags) { int rv = TCL_OK, non_ascii = FALSE; unsigned char c; BODY *body = NULL, *tbp = NULL; char errbuf[WP_MAX_POST_ERROR + 1], *charset; STRLIST_S *lp; if(err){ if(md->msgtext) so_give(&md->msgtext); rv = TCL_ERROR; } else if(md->qualified_addrs && check_addresses(md->metaenv) == CA_BAD){ sprintf(err = tmp_20k_buf, "Address must be fully qualified."); rv = TCL_ERROR; } else{ /* sniff body for possible multipart wrapping to protect encoding */ so_seek(md->msgtext, 0L, 0); while(so_readc(&c, md->msgtext)) if(!c || c & 0x80){ non_ascii = TRUE; break; } if(!md->outgoing->from) md->outgoing->from = generate_from(); rfc822_date(tmp_20k_buf); md->outgoing->date = (unsigned char *) cpystr(tmp_20k_buf); md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from); md->outgoing->message_id = generate_message_id(); body = mail_newbody(); /* wire any attachments to body */ if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global))){ PART **np; PARAMETER **pp; COMPATT_S *a; /* setup slot for message text */ body->type = TYPEMULTIPART; body->nested.part = mail_newbody_part(); tbp = &body->nested.part->body; /* link in attachments */ for(lp = md->attach, np = &body->nested.part->next; lp; lp = lp->next, np = &(*np)->next){ if(!(a = peGetAttachID(lp->name))){ err = "Unknown Attachment ID"; rv = TCL_ERROR; break; } *np = mail_newbody_part(); if(a->file){ (*np)->body.id = generate_message_id(); (*np)->body.description = cpystr(a->l.f.description); /* set name parameter */ for(pp = &(*np)->body.parameter; *pp; ) if(!struncmp((*pp)->attribute, "name", 4) && (!*((*pp)->attribute + 4) || *((*pp)->attribute + 4) == '*')){ PARAMETER *free_me = *pp; *pp = (*pp)->next; free_me->next = NULL; mail_free_body_parameter(&free_me); } else pp = &(*pp)->next; *pp = NULL; set_parameter(pp, "name", a->l.f.remote); /* Then set the Content-Disposition ala RFC1806 */ if(!(*np)->body.disposition.type){ (*np)->body.disposition.type = cpystr("attachment"); for(pp = &(*np)->body.disposition.parameter; *pp; ) if(!struncmp((*pp)->attribute, "filename", 4) && (!*((*pp)->attribute + 4) || *((*pp)->attribute + 4) == '*')){ PARAMETER *free_me = *pp; *pp = (*pp)->next; free_me->next = NULL; mail_free_body_parameter(&free_me); } else pp = &(*pp)->next; *pp = NULL; set_parameter(pp, "filename", a->l.f.remote); } if(((*np)->body.contents.text.data = (void *) so_get(FileStar, a->l.f.local, READ_ACCESS)) != NULL){ (*np)->body.type = mt_translate_type(a->l.f.type); (*np)->body.subtype = cpystr(a->l.f.subtype); (*np)->body.encoding = ENCBINARY; (*np)->body.size.bytes = name_file_size(a->l.f.local); if((*np)->body.type == TYPEOTHER && !set_mime_type_by_extension(&(*np)->body, a->l.f.local)) set_mime_type_by_grope(&(*np)->body); so_release((STORE_S *)(*np)->body.contents.text.data); } else{ /* unravel here */ err = "Can't open uploaded attachment"; rv = TCL_ERROR; break; } } else if(a->body){ BODY *newbody = copy_body(NULL, a->l.b.body); (*np)->body = *newbody; fs_give((void **) &newbody); peBodyMoveContents(a->l.b.body, &(*np)->body); } else{ err = "BOTCH: Unknown attachment type"; rv = TCL_ERROR; break; } } } else tbp = body; /* assign MIME parameters to text body part */ tbp->type = TYPETEXT; if(md->html) tbp->subtype = cpystr("HTML"); tbp->contents.text.data = (void *) md->msgtext; tbp->encoding = ENCOTHER; /* set any text flowed param */ if(md->flowed) peMsgSetParm(&tbp->parameter, "format", "flowed"); if(rv == TCL_OK){ CONTEXT_S *fcc_cntxt = ps_global->context_list; while(md->fcc_colid--) if(fcc_cntxt->next) fcc_cntxt = fcc_cntxt->next; if(md->postop_fcc_no_attach >= 0){ int oldval = F_ON(F_NO_FCC_ATTACH, ps_global); F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach); md->postop_fcc_no_attach = oldval; } pine_encode_body(body); rv = (*md->postfunc)(md->metaenv, body, md->fcc, &fcc_cntxt, errbuf); if(md->postop_fcc_no_attach >= 0){ F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach); } if(rv == TCL_OK){ if((flags & PMC_PRSRV_ATT) == 0) peFreeAttach(&peCompAttach); } else{ /* maintain pointers to attachments */ (void) peMsgAttachCollector(NULL, body); err = errbuf; } } pine_free_body(&body); } if(md->charset) fs_give((void **) &md->charset); free_strlist(&md->attach); pine_free_env(&md->metaenv); if(md->custom) free_customs(md->custom); mail_free_envelope(&md->outgoing); if(err && *err) Tcl_SetResult(interp, err, TCL_VOLATILE); return(rv); } void peMsgSetParm(PARAMETER **pp, char *pa, char *pv) { for(; *pp; pp = &(*pp)->next) if(!strucmp(pa, (*pp)->attribute)){ if((*pp)->value) fs_give((void **) &(*pp)->value); break; } if(!*pp){ *pp = mail_newbody_parameter(); (*pp)->attribute = cpystr(pa); } (*pp)->value = cpystr(pv); } Tcl_Obj * peMsgAttachCollector(Tcl_Interp *interp, BODY *b) { char *id, *name = NULL; PART *part; Tcl_Obj *aListObj = NULL, *aObj = NULL; peFreeAttach(&peCompAttach); if(interp) aListObj = Tcl_NewListObj(0, NULL); if(b->type == TYPEMULTIPART){ /* * Walk first level, clipping branches and adding them * to the attachment list... */ for(part = b->nested.part->next; part; part = part->next) { id = peBodyAttachID(&part->body); aObj = Tcl_NewListObj(0, NULL); if(interp){ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(id, -1)); /* name */ if((name = get_filename_parameter(NULL, 0, &part->body, NULL)) != NULL){ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(name, -1)); fs_give((void **) &name); } else Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj("Unknown", -1)); /* size */ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewLongObj((part->body.encoding == ENCBASE64) ? ((part->body.size.bytes * 3)/4) : part->body.size.bytes)); /* type */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", body_type_names(part->body.type), part->body.subtype ? part->body.subtype : rfc822_default_subtype (part->body.type)); Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(tmp_20k_buf, -1)); Tcl_ListObjAppendElement(interp, aListObj, aObj); } } } return (aListObj); } int peFccAppend(Tcl_Interp *interp, Tcl_Obj *obj, char *fcc, int colid) { Tcl_Obj *objfcc = NULL; if(colid < 0) colid = (ps_global->context_list && (ps_global->context_list->use & CNTXT_INCMNG)) ? 1 : 0; return((objfcc = Tcl_NewListObj(0, NULL)) && Tcl_ListObjAppendElement(interp, objfcc, Tcl_NewStringObj("fcc", -1)) == TCL_OK && peAppListF(interp, objfcc, "%i%s", colid, fcc) == TCL_OK && Tcl_ListObjAppendElement(interp, obj, objfcc) == TCL_OK); } /* * * * * * * * * * * * * Start of Address Management Routines * * * * * * * * * * * */ /* * PEAddressCmd - export various bits of address book/directory access */ int PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *op; dprint((2, "PEAddressCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); return(TCL_ERROR); } else if(!ps_global){ Tcl_SetResult(interp, "PEAddress: no open folder", TCL_STATIC); return(TCL_ERROR); } else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){ if(objc == 2){ if(!strcmp(op, "safecheck")){ if(peInitAddrbooks(interp, 1) != TCL_OK) return(TCL_ERROR); return(TCL_OK); } else if(!strcmp(op, "books")){ int i; /* * return the list of configured address books */ if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); for(i = 0; i < as.n_addrbk; i++){ Tcl_Obj *objmv[4]; objmv[0] = Tcl_NewIntObj(i); if(as.adrbks[i].abnick){ objmv[1] = Tcl_NewStringObj(as.adrbks[i].abnick, -1); } else { char buf[256]; snprintf(buf, sizeof(buf), "Address book number %d", i + 1); objmv[1] = Tcl_NewStringObj(buf, -1); } objmv[2] = Tcl_NewStringObj(as.adrbks[i].filename ? as.adrbks[i].filename : "", -1); objmv[3] = Tcl_NewIntObj(as.adrbks[i].access == ReadWrite); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(4, objmv)); } return(TCL_OK); } } else if(objc == 3){ if(!strcmp(op, "parselist")){ char *addrstr; ADDRESS *addrlist = NULL, *atmp, *anextp; static char *fakedomain = "@"; if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL) addrstr = cpystr(addrstr); /* can't munge tcl copy */ ps_global->c_client_error[0] = '\0'; rfc822_parse_adrlist(&addrlist, addrstr, (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) ? fakedomain : ps_global->maildomain); fs_give((void **) &addrstr); if(ps_global->c_client_error[0]){ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC); return(TCL_ERROR); } for(atmp = addrlist; atmp; ){ RFC822BUFFER rbuf; anextp = atmp->next; atmp->next = NULL; tmp_20k_buf[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = tmp_20k_buf; rbuf.cur = tmp_20k_buf; rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1; rfc822_output_address_list(&rbuf, atmp, 0L, NULL); *rbuf.cur = '\0'; Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1)); atmp = anextp; } mail_free_address(&addrlist); return(TCL_OK); } else if(!strcmp(op, "xlookup")){ char *addrstr; ADDRESS *addrlist = NULL, *atmp, *anextp; static char *fakedomain = "@"; if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL) addrstr = cpystr(addrstr); /* can't munge tcl copy */ ps_global->c_client_error[0] = '\0'; rfc822_parse_adrlist(&addrlist, addrstr, (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) ? fakedomain : ps_global->maildomain); fs_give((void **) &addrstr); if(ps_global->c_client_error[0]){ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC); return(TCL_ERROR); } for(atmp = addrlist; atmp; ){ anextp = atmp->next; atmp->next = NULL; tmp_20k_buf[0] = '\0'; if(atmp->host){ if(atmp->host[0] == '@'){ /* leading ampersand means "missing-hostname" */ } else{ RFC822BUFFER rbuf; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = tmp_20k_buf; rbuf.cur = tmp_20k_buf; rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1; rfc822_output_address_list(&rbuf, atmp, 0L, NULL); *rbuf.cur = '\0'; Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1)); } } /* else group syntax, move on */ atmp = anextp; } mail_free_address(&addrlist); return(TCL_OK); } else if(!strcmp(op, "format")){ int i, booknum; char buf[256], *s; if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); /* * */ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK) for(i = 0; i < as.n_addrbk; i++) if(i == booknum){ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL); for(i = 0; i < NFIELDS && as.adrbks[booknum].disp_form[i].type != Notused; i++){ switch(as.adrbks[booknum].disp_form[i].type){ case Nickname : s = "nick"; break; case Fullname : s = "full"; break; case Addr : s = "addr"; break; case Filecopy : s = "fcc"; break; case Comment : s = "comment"; break; default : s = NULL; break; } if(s){ Tcl_Obj *objmv[2]; objmv[0] = Tcl_NewStringObj(s, -1); objmv[1] = Tcl_NewIntObj((100 * as.adrbks[booknum].disp_form[i].width) / ps_global->ttyo->screen_cols); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(2, objmv)); } } return(TCL_OK); } snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } else if(!strcmp(op, "list")){ int i, j, k, n, booknum; char buf[256], *s; AdrBk_Entry *ae; Tcl_Obj *objev[NFIELDS + 1], *objhv[2]; if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); /* * */ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK) for(i = 0; i < as.n_addrbk; i++) if(i == booknum){ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL); for(i = 0; (ae = adrbk_get_ae(as.adrbks[booknum].address_book, i)); i++){ /* first member is type: Single, List or Lookup */ switch(ae->tag){ case Single : s = "single"; break; case List : s = "list"; break; default : /* not set!?! */ continue; } if(!ae->nickname) continue; objhv[0] = Tcl_NewStringObj(ae->nickname, -1); objhv[1] = Tcl_NewStringObj(s, -1); objev[n = 0] = Tcl_NewListObj(2, objhv); /* * set fields based on VAR_ABOOK_FORMATS */ for(j = 0; j < NFIELDS && as.adrbks[booknum].disp_form[j].type != Notused; j++){ switch(as.adrbks[booknum].disp_form[j].type){ case Nickname : objev[++n] = Tcl_NewStringObj(ae->nickname, -1); break; case Fullname : objev[++n] = Tcl_NewStringObj(ae->fullname, -1); break; case Addr : if(ae->tag == Single){ objev[++n] = Tcl_NewStringObj(ae->addr.addr, -1); } else{ Tcl_Obj **objav; for(k = 0; ae->addr.list[k]; k++) ; objav = (Tcl_Obj **) fs_get(k * sizeof(Tcl_Obj *)); for(k = 0; ae->addr.list[k]; k++) objav[k] = Tcl_NewStringObj(ae->addr.list[k], -1); objev[++n] = Tcl_NewListObj(k, objav); fs_give((void **) &objav); } break; case Filecopy : objev[++n] = Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1); break; case Comment : objev[++n] = Tcl_NewStringObj(ae->extra ? ae->extra : "", -1); break; default : s = NULL; break; } } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewListObj(n + 1, objev)); } return(TCL_OK); } snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } } else if(objc == 4){ if(!strcmp(op, "verify")){ /* * The object here is to check the following list of field values * to see that they are valid address list, expanding if necessary. * The first argument is the list of field values, with "to" being * first. The second arg is the current fcc value. * * The return value is of the following form: * * { {{errstr {{oldstr newstr {ldap-opts ...}} ...}} ...} newfcc} */ Tcl_Obj **objVal; char *addrstr, *newaddr = NULL, *error = NULL, *tstr1, *tstr2, *fcc, *newfcc = NULL; BuildTo toaddr; int rv, badadrs, i , numlistvals, numldapqueries = 0; Tcl_Obj *resObj = NULL, *secObj, *strObj, *adrObj, *res2Obj; #ifdef ENABLE_LDAP WPLDAPRES_S **tsl; wpldap_global->query_no++; if(wpldap_global->ldap_search_list){ wpldap_global->ldap_search_list = free_wpldapres(wpldap_global->ldap_search_list); } tsl = &(wpldap_global->ldap_search_list); #endif /* ENABLE_LDAP */ if(Tcl_ListObjGetElements(interp, objv[2], &numlistvals, &objVal) == TCL_OK){ if((fcc = Tcl_GetStringFromObj(objv[3], NULL)) == NULL) return TCL_ERROR; res2Obj = Tcl_NewListObj(0, NULL); for(i = 0; i < numlistvals; i++){ size_t l; if((addrstr = Tcl_GetStringFromObj(objVal[i], NULL)) == NULL) return TCL_ERROR; addrstr = cpystr(addrstr); /* can't munge tcl copy */ toaddr.type = Str; toaddr.arg.str = cpystr(addrstr); l = strlen(addrstr); badadrs = 0; resObj = Tcl_NewListObj(0, NULL); secObj = Tcl_NewListObj(0, NULL); for(tstr1 = addrstr; tstr1; tstr1 = tstr2){ tstr2 = strqchr(tstr1, ',', 0, -1); if(tstr2) *tstr2 = '\0'; strncpy(toaddr.arg.str, tstr1, l); toaddr.arg.str[l] = '\0'; removing_leading_and_trailing_white_space(toaddr.arg.str); if(*toaddr.arg.str){ if(i == 0 && tstr1 == addrstr) newfcc = cpystr(fcc); rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL); if(rv == 0){ strObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, strObj, Tcl_NewStringObj(toaddr.arg.str, -1)); Tcl_ListObjAppendElement(interp, strObj, Tcl_NewStringObj(newaddr,-1)); /* append whether or not ldap stuff * was returned */ adrObj = Tcl_NewListObj(0,NULL); #ifdef ENABLE_LDAP if(*tsl) { LDAP_CHOOSE_S *tres; LDAP_SERV_RES_S *trl; LDAPMessage *e; ADDRESS *newadr; char *ret_to; tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S)); for(trl = (*tsl)->reslist; trl; trl = trl->next){ for(e = ldap_first_entry(trl->ld, trl->res); e != NULL; e = ldap_next_entry(trl->ld, e)){ tres->ld = trl->ld; tres->selected_entry = e; tres->info_used = trl->info_used; tres->serv = trl->serv; if((newadr = address_from_ldap(tres)) != NULL){ if(newadr->mailbox && newadr->host){ RFC822BUFFER rbuf; size_t len; len = est_size(newadr); ret_to = (char *)fs_get(len * sizeof(char)); ret_to[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = ret_to; rbuf.cur = ret_to; rbuf.end = ret_to+len-1; rfc822_output_address_list(&rbuf, newadr, 0L, NULL); *rbuf.cur = '\0'; Tcl_ListObjAppendElement(interp, adrObj, Tcl_NewStringObj(ret_to, -1)); fs_give((void **)&ret_to); } mail_free_address(&newadr); } } } fs_give((void **)&tres); numldapqueries++; tsl = &((*tsl)->next); } #endif /* ENABLE_LDAP */ Tcl_ListObjAppendElement(interp, strObj, adrObj); Tcl_ListObjAppendElement(interp, secObj, strObj); } else { badadrs = 1; break; } } if(tstr2){ *tstr2 = ','; tstr2++; } } resObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(badadrs ? (error ? error : "Unknown") : "", -1)); Tcl_ListObjAppendElement(interp, resObj, secObj); Tcl_ListObjAppendElement(interp, res2Obj, resObj); fs_give((void **) &addrstr); fs_give((void **) &toaddr.arg.str); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), res2Obj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(newfcc ? newfcc : (fcc ? fcc : ""), -1)); if(newfcc) fs_give((void **)&newfcc); return(TCL_OK); } return(TCL_ERROR); } else if(!strcmp(op, "expand")){ BuildTo toaddr; char *addrstr, *newaddr = NULL, *error = NULL, *fcc, *newfcc = NULL; int rv; /* * Return value will be of the form: * {"addrstr", * ldap-query-number, * "fcc" * } * * ldap-query-number will be nonzero if * there is something interesting to display as a result * of an ldap query. */ /* * Given what looks like an rfc822 address line, parse the * contents and expand any tokens that look like they ought * to be. */ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ toaddr.type = Str; toaddr.arg.str = cpystr(addrstr); /* can't munge tcl copy */ fcc = Tcl_GetStringFromObj(objv[3], NULL); #ifdef ENABLE_LDAP wpldap_global->query_no++; if(wpldap_global->ldap_search_list){ wpldap_global->ldap_search_list = free_wpldapres(wpldap_global->ldap_search_list); } #endif /* ENABLE_LDAP */ newfcc = cpystr(fcc); rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL); fs_give((void **) &toaddr.arg.str); if(rv == 0){ #ifdef ENABLE_LDAP /* * c-client quotes results with spaces in them, so we'll go * through and unquote them. */ if(wpldap_global->ldap_search_list){ WPLDAPRES_S *tres; char *tstr1, *tstr2; char *qstr1, *newnewaddr; int qstr1len; for(tres = wpldap_global->ldap_search_list; tres; tres = tres->next){ if(strqchr(tres->str, ' ', 0, -1)){ qstr1len = strlen(tres->str) + 3; qstr1 = (char *)fs_get(qstr1len*sizeof(char)); snprintf(qstr1, qstr1len, "\"%.*s\"", qstr1len, tres->str); for(tstr1 = newaddr; tstr1; tstr1 = tstr2){ tstr2 = strqchr(tstr1, ',', 0, -1); if(strncmp(qstr1, tstr1, tstr2 ? tstr2 - tstr1 : strlen(tstr1)) == 0){ size_t l; l = strlen(newaddr) + strlen(tres->str) + 2 + (tstr2 ? strlen(tstr2) : 0); newnewaddr = (char *) fs_get(l * sizeof(char)); snprintf(newnewaddr, l, "%.*s%s%s", (int) (tstr1 - newaddr), newaddr, tres->str, tstr2 ? tstr2 : ""); fs_give((void **)&newaddr); newaddr = newnewaddr; break; } if(tstr2) tstr2++; if(tstr2 && *tstr2 == ' ') tstr2++; } if(qstr1) fs_give((void **) &qstr1); } } } #endif /* ENABLE_LDAP */ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(newaddr, -1)) != TCL_OK) return(TCL_ERROR); #ifdef ENABLE_LDAP if(wpldap_global->ldap_search_list){ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(wpldap_global->query_no)) != TCL_OK) return(TCL_ERROR); } else #endif /* ENABLE_LDAP */ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(0)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(newfcc ? newfcc : (fcc ? fcc : ""), -1)) != TCL_OK) return(TCL_ERROR); if(newfcc) fs_give((void **)&newfcc); return(TCL_OK); } else{ Tcl_SetResult(interp, error ? error : "Indeterminate error", TCL_VOLATILE); if(newfcc) fs_give((void **)&newfcc); return(TCL_ERROR); } } } else if(!strcmp(op, "complete")){ /* * CMD: complete uid * * Look for possible completions for * given query_string. * * ARGS: * * Returns: candidate list: {nickname {personal mailbox}} */ char *query, *errstr; long uid; COMPLETE_S *completions, *cp; if(peInitAddrbooks(interp, 0) == TCL_OK){ if((query = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){ if(Tcl_GetLongFromObj(interp, objv[3], &uid) == TCL_OK){ completions = adrbk_list_of_completions(query, ps_global->mail_stream, uid, #ifdef ENABLE_LDAP ((strlen(query) >= 5) ? ALC_INCLUDE_LDAP : 0) | #endif /* ENABLE_LDAP */ 0); if(completions){ for(cp = completions; cp; cp = cp->next) peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s", cp->nickname ? cp->nickname : "", cp->full_address ? cp->full_address : "", cp->fcc ? cp->fcc : ""); free_complete_s(&completions); } else Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } else errstr = "PEAddress: Cannot read UID"; } else errstr = "PEAddress: Cannot get completion query"; } else errstr = "PEAddress: Address Book initialization failed"; Tcl_SetResult(interp, errstr, TCL_STATIC); return(TCL_ERROR); } } else if(objc == 5){ if(!strcmp(op, "entry")){ int booknum, i, aindex; char *nick, *astr = NULL, *errstr = NULL, *fccstr = NULL, buf[128]; AdrBk_Entry *ae; BuildTo bldto; if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); /* * Given an address book handle and nickname, return address */ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK) for(i = 0; i < as.n_addrbk; i++) if(i == booknum){ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL){ Tcl_SetResult(interp, "PEAddress list: Can't get nickname", TCL_STATIC); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK || (*nick == '\0' && aindex < 0)){ Tcl_SetResult(interp, "PEAddress list: Can't get aindex", TCL_STATIC); return(TCL_ERROR); } if((*nick) ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL)) : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){ bldto.type = Abe; bldto.arg.abe = ae; (void) our_build_address(bldto, &astr, &errstr, &fccstr, NULL); if(errstr){ if(astr) fs_give((void **) &astr); Tcl_SetResult(interp, errstr, TCL_VOLATILE); return(TCL_ERROR); } if(astr){ char *p; int l; l = (4*strlen(astr) + 1) * sizeof(char); p = (char *) fs_get(l); if(rfc1522_decode_to_utf8((unsigned char *) p, l, astr) == (unsigned char *) p){ fs_give((void **) &astr); astr = p; } else fs_give((void **)&p); } } if(astr){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(astr, -1)); fs_give((void **) &astr); if(fccstr){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(*fccstr ? fccstr : "\"\"", -1)); fs_give((void **) &fccstr); } else Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("", -1)); } else Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } snprintf(buf, sizeof(buf), "PEAddress list: unknown address book ID %d", booknum); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } else if(!strcmp(op, "fullentry")){ int booknum, j, aindex; char *nick; AdrBk_Entry *ae; Tcl_Obj *resObj; if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); /* * Given an address book handle and nickname, return * nickname, fullname, address(es), fcc, and comments */ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){ if(booknum >= 0 && booknum < as.n_addrbk){ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL) return(TCL_ERROR); if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK || (*nick == '\0' && aindex < 0)) return(TCL_ERROR); if((*nick) ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL)) : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ae->nickname ? ae->nickname : "", -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ae->fullname ? ae->fullname : "", -1)); resObj = Tcl_NewListObj(0,NULL); if(ae->tag == Single) Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(ae->addr.addr ? ae->addr.addr : "", -1)); else { for(j = 0; ae->addr.list[j]; j++) Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(ae->addr.list[j] ? ae->addr.list[j] : "", -1)); } Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1)); Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ae->extra ? ae->extra : "", -1)); return(TCL_OK); } } } return(TCL_ERROR); } else if(!strcmp(op, "delete")){ char *nick, buf[256]; int booknum, aindex; adrbk_cntr_t old_entry; AdrBk *ab; if(peInitAddrbooks(interp, 0) != TCL_OK){ snprintf(buf, sizeof(buf), "PEAddress delete: couldn't init addressbooks"); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){ nick = Tcl_GetStringFromObj(objv[3], NULL); removing_leading_and_trailing_white_space(nick); } else return(TCL_ERROR); if(booknum >= 0 && booknum < as.n_addrbk) { if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR; ab = as.adrbks[booknum].address_book; } else{ snprintf(buf, sizeof(buf), "PEAddress delete: Book number out of range"); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } if((Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK) || (*nick == '\0' && aindex < 0)) return(TCL_ERROR); adrbk_check_validity(ab, 1L); if(ab->flags & FILE_OUTOFDATE || (ab->rd && ab->rd->flags & REM_OUTOFDATE)){ Tcl_SetResult(interp, "Address book out of sync. Cannot update at this moment", TCL_VOLATILE); return(TCL_ERROR); } if(!nick){ snprintf(buf, sizeof(buf), "PEAddress delete: No nickname"); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } if((*nick) ? (!adrbk_lookup_by_nick(ab, nick, &old_entry)) : ((old_entry = (adrbk_cntr_t)aindex) == -1)){ snprintf(buf, sizeof(buf), "PEAddress delete: Nickname \"%.128s\" not found", nick); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } if(adrbk_delete(ab, old_entry, 0, 0, 1, 1)){ snprintf(buf, sizeof(buf), "PEAddress delete: Couldn't delete addressbook entry"); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } return(TCL_OK); } } else if((objc == 10 || objc == 11) && !strcmp(op, "edit")){ if(!strcmp(op, "edit")){ int booknum, adri, add, rv, aindex; char *nick, *fn, *fcc, *comment, *addrfield, buf[256], **addrs, *orignick = NULL; AdrBk_Entry *ae = NULL; AdrBk *ab; adrbk_cntr_t old_entry = NO_NEXT, new_entry; Tag tag; ADDRESS *adr = NULL; if(peInitAddrbooks(interp, 0) != TCL_OK) return(TCL_ERROR); if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){ if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR; nick = Tcl_GetStringFromObj(objv[3], NULL); removing_leading_and_trailing_white_space(nick); if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK){ Tcl_SetResult(interp, "No Address Handle", TCL_VOLATILE); return(TCL_ERROR); } fn = Tcl_GetStringFromObj(objv[5], NULL); removing_leading_and_trailing_white_space(fn); if(!*fn) fn = NULL; addrfield = Tcl_GetStringFromObj(objv[6], NULL); removing_leading_and_trailing_white_space(addrfield); if(!*addrfield) addrfield = NULL; /* if(Tcl_ListObjGetElements(interp, objv[7], &numlistvals, &objVal) != TCL_OK) return(TCL_ERROR); */ fcc = Tcl_GetStringFromObj(objv[7], NULL); removing_leading_and_trailing_white_space(fcc); if(!*fcc) fcc = NULL; comment = Tcl_GetStringFromObj(objv[8], NULL); removing_leading_and_trailing_white_space(comment); if(!*comment) comment = NULL; if(Tcl_GetIntFromObj(interp, objv[9], &add) != TCL_OK) return(TCL_ERROR); if(objc == 11) { /* * if objc == 11 then that means that they changed the * value of nick to something else, and this one is the * original nick */ orignick = Tcl_GetStringFromObj(objv[10], NULL); removing_leading_and_trailing_white_space(orignick); } if((addrs = parse_addrlist(addrfield)) != NULL){ int tbuflen = strlen(addrfield); char *tbuf; if(!(tbuf = (char *) fs_get(sizeof(char) * (tbuflen+128)))){ Tcl_SetResult(interp, "malloc error", TCL_VOLATILE); fs_give((void **) &addrs); return(TCL_ERROR); } for(adri = 0; addrs[adri]; adri++){ if(*(addrs[adri])){ ps_global->c_client_error[0] = '\0'; strncpy(tbuf, addrs[adri], tbuflen+128); tbuf[tbuflen+128-1] = '\0'; rfc822_parse_adrlist(&adr, tbuf, "@"); if(adr) mail_free_address(&adr); adr = NULL; if(ps_global->c_client_error[0]){ snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s", addrs[adri], strlen(addrs[adri]) > 10 ? "..." : "", ps_global->c_client_error); Tcl_SetResult(interp, buf, TCL_VOLATILE); if(tbuf) fs_give((void **) &tbuf); fs_give((void **) &addrs); return(TCL_ERROR); } } } if(tbuf) fs_give((void **)&tbuf); } else adri = 0; /* addrs[adri] = NULL; */ if(adri > 1) tag = List; else tag = Single; if(booknum >= 0 && booknum < as.n_addrbk) { ab = as.adrbks[booknum].address_book; } else{ if(addrs) fs_give((void **) &addrs); return(TCL_ERROR); } adrbk_check_validity(ab, 1L); if(ab->flags & FILE_OUTOFDATE || (ab->rd && ab->rd->flags & REM_OUTOFDATE)){ Tcl_SetResult(interp, "Address book out of sync. Cannot update at this moment", TCL_VOLATILE); return(TCL_ERROR); } if(aindex >= 0){ ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex); if(ae){ old_entry = (adrbk_cntr_t) aindex; } else{ Tcl_SetResult(interp, "No Address Handle!", TCL_VOLATILE); return(TCL_ERROR); } } else if(nick && *nick && adrbk_lookup_by_nick(ab, nick, NULL)){ snprintf(buf, sizeof(buf), "Entry with nickname %.128s already exists.", nick); Tcl_SetResult(interp, buf, TCL_VOLATILE); if(addrs) fs_give((void **) &addrs); return(TCL_ERROR); } if(ae && ((tag == List && ae->tag == Single) || (tag == Single && ae->tag == List))){ if(adrbk_delete(ab, old_entry, 0,0,1,0)){ snprintf(buf, sizeof(buf), "Problem updating from %s to %s.", ae->tag == Single ? "Single" : "List", tag == List ? "List" : "Single"); Tcl_SetResult(interp, buf, TCL_VOLATILE); if(addrs) fs_give((void **) &addrs); return(TCL_ERROR); } old_entry = NO_NEXT; } if((rv = adrbk_add(ab, old_entry, nick ? nick : "", fn ? fn : "", tag == List ? (char *)addrs : (addrs && *addrs) ? *addrs : "", fcc ? fcc : "", comment ? comment : "", tag, &new_entry, NULL, 0, 1, tag == List ? 0 : 1)) != 0){ snprintf(buf, sizeof(buf), "Couldn't add entry! rv=%d.", rv); Tcl_SetResult(interp, buf, TCL_VOLATILE); if(addrs) fs_give((void **) &addrs); return(TCL_ERROR); } if(tag == List) { adrbk_listdel_all(ab, new_entry); adrbk_nlistadd(ab, new_entry, NULL, NULL, addrs, 0, 1, 1); } return(TCL_OK); } snprintf(buf, sizeof(buf), "Unknown address book ID %d", booknum); Tcl_SetResult(interp, buf, TCL_VOLATILE); return(TCL_ERROR); } } } Tcl_SetResult(interp, "PEAddress: unrecognized command", TCL_STATIC); return(TCL_ERROR); } int peInitAddrbooks(Tcl_Interp *interp, int safe) { if(ps_global->remote_abook_validity > 0) (void)adrbk_check_and_fix_all(safe, 0, 0); if(!init_addrbooks(NoDisplay, 1, 1, 0)){ Tcl_SetResult(interp, "No Address Book Configured", TCL_STATIC); return(TCL_ERROR); } return(TCL_OK); } int peRuleStatVal(char *str, int *n) { if(!str) return(1); if(!strcmp(str, "either")) *n = PAT_STAT_EITHER; else if(!strcmp(str, "yes")) *n = PAT_STAT_YES; else if(!strcmp(str, "no")) *n = PAT_STAT_NO; else return 1; return 0; } #define RS_RULE_EDIT 0x0001 #define RS_RULE_ADD 0x0002 #define RS_RULE_DELETE 0x0004 #define RS_RULE_SHUFFUP 0x0008 #define RS_RULE_SHUFFDOWN 0x0010 #define RS_RULE_GETPAT 0x0100 #define RS_RULE_FINDPAT 0x0200 int peRuleSet(Tcl_Interp *interp, Tcl_Obj **objv) { char *rule, *patvar, *patval, *actvar, *actval, *tstr, *ruleaction; int rno, nPat, nPatEmnt, nAct, nActEmnt, i, rv = 0; Tcl_Obj **objPat, **objPatEmnt, **objAct, **objActEmnt; long rflags = PAT_USE_CHANGED, aflags = 0; PAT_STATE pstate; PAT_S *pat, *new_pat; if(!(rule = Tcl_GetStringFromObj(objv[0], NULL))) return(TCL_ERROR); if(!(ruleaction = Tcl_GetStringFromObj(objv[1], NULL))) return(TCL_ERROR); if(Tcl_GetIntFromObj(interp, objv[2], &rno) == TCL_ERROR) return(TCL_ERROR); if(!(strcmp(rule, "filter"))) rflags |= ROLE_DO_FILTER; else if(!(strcmp(rule, "score"))) rflags |= ROLE_DO_SCORES; else if(!(strcmp(rule, "indexcolor"))) rflags |= ROLE_DO_INCOLS; else return(TCL_ERROR); if(!(strcmp(ruleaction, "edit"))){ aflags |= RS_RULE_EDIT; aflags |= RS_RULE_GETPAT; aflags |= RS_RULE_FINDPAT; } else if(!(strcmp(ruleaction, "add"))){ aflags |= RS_RULE_ADD; aflags |= RS_RULE_GETPAT; } else if(!(strcmp(ruleaction, "delete"))){ aflags |= RS_RULE_DELETE; aflags |= RS_RULE_FINDPAT; } else if(!(strcmp(ruleaction, "shuffup"))){ aflags |= RS_RULE_SHUFFUP; aflags |= RS_RULE_FINDPAT; } else if(!(strcmp(ruleaction, "shuffdown"))){ aflags |= RS_RULE_SHUFFDOWN; aflags |= RS_RULE_FINDPAT; } else return(TCL_ERROR); if(aflags & RS_RULE_FINDPAT){ if(any_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate), i = 0; pat && i != rno; pat = next_pattern(&pstate), i++); if(i != rno) return(TCL_ERROR); } } if(aflags & RS_RULE_GETPAT){ int tcl_error = 0; Tcl_ListObjGetElements(interp, objv[3], &nPat, &objPat); Tcl_ListObjGetElements(interp, objv[4], &nAct, &objAct); new_pat = (PAT_S *)fs_get(sizeof(PAT_S)); memset(new_pat, 0, sizeof(PAT_S)); new_pat->patgrp = (PATGRP_S *)fs_get(sizeof(PATGRP_S)); memset(new_pat->patgrp, 0, sizeof(PATGRP_S)); new_pat->action = (ACTION_S *)fs_get(sizeof(ACTION_S)); memset(new_pat->action, 0, sizeof(ACTION_S)); /* Set up the pattern group */ for(i = 0; i < nPat; i++){ Tcl_ListObjGetElements(interp, objPat[i], &nPatEmnt, &objPatEmnt); if(nPatEmnt != 2) return(TCL_ERROR); patvar = Tcl_GetStringFromObj(objPatEmnt[0], NULL); patval = Tcl_GetStringFromObj(objPatEmnt[1], NULL); if(!patvar || !patval) return(TCL_ERROR); tstr = NULL; if(*patval){ tstr = cpystr(patval); removing_leading_and_trailing_white_space(tstr); if(!(*tstr)) fs_give((void **) &tstr); } if(!(strcmp(patvar, "nickname"))){ new_pat->patgrp->nick = tstr; tstr = NULL; } else if(!(strcmp(patvar, "comment"))){ new_pat->patgrp->comment = tstr; tstr = NULL; } else if(!(strcmp(patvar, "to"))){ new_pat->patgrp->to = string_to_pattern(tstr); } else if(!(strcmp(patvar, "from"))){ new_pat->patgrp->from = string_to_pattern(tstr); } else if(!(strcmp(patvar, "sender"))){ new_pat->patgrp->sender = string_to_pattern(tstr); } else if(!(strcmp(patvar, "cc"))){ new_pat->patgrp->cc = string_to_pattern(tstr); } else if(!(strcmp(patvar, "recip"))){ new_pat->patgrp->recip = string_to_pattern(tstr); } else if(!(strcmp(patvar, "partic"))){ new_pat->patgrp->partic = string_to_pattern(tstr); } else if(!(strcmp(patvar, "news"))){ new_pat->patgrp->news = string_to_pattern(tstr); } else if(!(strcmp(patvar, "subj"))){ new_pat->patgrp->subj = string_to_pattern(tstr); } else if(!(strcmp(patvar, "bodytext"))){ new_pat->patgrp->bodytext = string_to_pattern(tstr); } else if(!(strcmp(patvar, "alltext"))){ new_pat->patgrp->alltext = string_to_pattern(tstr); } else if(!(strcmp(patvar, "keyword"))){ new_pat->patgrp->keyword = string_to_pattern(tstr); } else if(!(strcmp(patvar, "charset"))){ new_pat->patgrp->charsets = string_to_pattern(tstr); } else if(!(strcmp(patvar, "ftype"))){ if(!tstr) return(TCL_ERROR); if(!(strcmp(tstr, "any"))) new_pat->patgrp->fldr_type = FLDR_ANY; else if(!(strcmp(tstr, "news"))) new_pat->patgrp->fldr_type = FLDR_NEWS; else if(!(strcmp(tstr, "email"))) new_pat->patgrp->fldr_type = FLDR_EMAIL; else if(!(strcmp(tstr, "specific"))) new_pat->patgrp->fldr_type = FLDR_SPECIFIC; else{ free_pat(&new_pat); return(TCL_ERROR); } } else if(!(strcmp(patvar, "folder"))){ new_pat->patgrp->folder = string_to_pattern(tstr); } else if(!(strcmp(patvar, "stat_new"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_new)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_rec"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_rec)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_del"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_del)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_imp"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_imp)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_ans"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_ans)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_8bitsubj"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_8bitsubj)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_bom"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_bom)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "stat_boy"))){ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_boy)){ free_pat(&new_pat); tcl_error++; } } else if(!(strcmp(patvar, "age"))){ new_pat->patgrp->age = parse_intvl(tstr); } else if(!(strcmp(patvar, "size"))){ new_pat->patgrp->size = parse_intvl(tstr); } else if(!(strcmp(patvar, "score"))){ new_pat->patgrp->score = parse_intvl(tstr); } else if(!(strcmp(patvar, "addrbook"))){ if(tstr){ if(!strcmp(tstr, "either")) new_pat->patgrp->inabook = IAB_EITHER; else if(!strcmp(tstr, "yes")) new_pat->patgrp->inabook = IAB_YES; else if(!strcmp(tstr, "no")) new_pat->patgrp->inabook = IAB_NO; else if(!strcmp(tstr, "yesspecific")) new_pat->patgrp->inabook = IAB_SPEC_YES; else if(!strcmp(tstr, "nospecific")) new_pat->patgrp->inabook = IAB_SPEC_NO; else tcl_error++; } else tcl_error++; if(tcl_error) free_pat(&new_pat); } else if(!(strcmp(patvar, "specificabook"))){ new_pat->patgrp->abooks = string_to_pattern(tstr); } else if(!(strcmp(patvar, "headers"))){ ARBHDR_S **ahp; int nHdrList, nHdrPair, n; Tcl_Obj **objHdrList, **objHdrPair; Tcl_ListObjGetElements(interp, objPatEmnt[1], &nHdrList, &objHdrList); for(ahp = &new_pat->patgrp->arbhdr; *ahp; ahp = &(*ahp)->next) ; for (n = 0; n < nHdrList; n++){ char *hdrfld; char *hdrval; Tcl_ListObjGetElements(interp, objHdrList[n], &nHdrPair, &objHdrPair); if(nHdrPair != 2) continue; hdrfld = Tcl_GetStringFromObj(objHdrPair[0], NULL); hdrval = Tcl_GetStringFromObj(objHdrPair[1], NULL); if(hdrfld){ *ahp = (ARBHDR_S *) fs_get(sizeof(ARBHDR_S)); memset(*ahp, 0, sizeof(ARBHDR_S)); (*ahp)->field = cpystr(hdrfld); if(hdrval){ (*ahp)->p = string_to_pattern(hdrval); } else (*ahp)->isemptyval = 1; ahp = &(*ahp)->next; } } } else{ free_pat(&new_pat); tcl_error++; } if(tstr) fs_give((void **) &tstr); if(tcl_error) return(TCL_ERROR); } if((new_pat->patgrp->inabook & (IAB_SPEC_YES | IAB_SPEC_NO)) == 0 && new_pat->patgrp->abooks) free_pattern(&new_pat->patgrp->abooks); if(new_pat->patgrp->fldr_type != FLDR_SPECIFIC && new_pat->patgrp->folder) free_pattern(&new_pat->patgrp->folder); /* set up the action */ if(!(strcmp(rule, "filter"))) new_pat->action->is_a_filter = 1; else if(!(strcmp(rule, "role"))) new_pat->action->is_a_role = 1; else if(!(strcmp(rule, "score"))) new_pat->action->is_a_score = 1; else if(!(strcmp(rule, "indexcolor"))) new_pat->action->is_a_incol = 1; else{ free_pat(&new_pat); return(TCL_ERROR); } for(i = 0; i < nAct; i++){ Tcl_ListObjGetElements(interp, objAct[i], &nActEmnt, &objActEmnt); if(nActEmnt !=2){ free_pat(&new_pat); return(TCL_ERROR); } actvar = Tcl_GetStringFromObj(objActEmnt[0], NULL); actval = Tcl_GetStringFromObj(objActEmnt[1], NULL); if(!actvar || !actval){ free_pat(&new_pat); return(TCL_ERROR); } if(new_pat->action->is_a_filter && !(strcmp(actvar, "action"))){ if(!strcmp(actval, "delete")) new_pat->action->kill = 1; else if(!strcmp(actval, "move")) new_pat->action->kill = 0; else{ free_pat(&new_pat); return(TCL_ERROR); } } else if(new_pat->action->is_a_filter && !(strcmp(actvar, "folder"))){ tstr = cpystr(actval); removing_leading_and_trailing_white_space(tstr); if(!(*tstr)) fs_give((void **)&tstr); new_pat->action->folder = string_to_pattern(tstr); if(tstr) fs_give((void **)&tstr); } else if(new_pat->action->is_a_filter && !(strcmp(actvar, "moind"))){ if(!strcmp(actval, "1")) new_pat->action->move_only_if_not_deleted = 1; else if(!strcmp(actval, "0")) new_pat->action->move_only_if_not_deleted = 0; else{ free_pat(&new_pat); return(TCL_ERROR); } } else if(new_pat->action->is_a_incol && !(strcmp(actvar, "fg"))){ char asciicolor[256]; if(ascii_colorstr(asciicolor, actval) == 0) { if(!new_pat->action->incol){ new_pat->action->incol = new_color_pair(asciicolor,NULL); } else snprintf(new_pat->action->incol->fg, sizeof(new_pat->action->incol->fg), "%s", asciicolor); } } else if(new_pat->action->is_a_incol && !(strcmp(actvar, "bg"))){ char asciicolor[256]; if(ascii_colorstr(asciicolor, actval) == 0) { if(!new_pat->action->incol){ new_pat->action->incol = new_color_pair(NULL, asciicolor); } else snprintf(new_pat->action->incol->bg, sizeof(new_pat->action->incol->bg), "%s", asciicolor); } } else if(new_pat->action->is_a_score && !(strcmp(actvar, "scoreval"))){ long scoreval = (long) atoi(actval); if(scoreval >= SCORE_MIN && scoreval <= SCORE_MAX) new_pat->action->scoreval = scoreval; } else if(new_pat->action->is_a_score && !(strcmp(actvar, "scorehdr"))){ HEADER_TOK_S *hdrtok; if((hdrtok = stringform_to_hdrtok(actval)) != NULL) new_pat->action->scorevalhdrtok = hdrtok; } else{ free_pat(&new_pat); return(TCL_ERROR); } } if(new_pat->action->is_a_filter && new_pat->action->kill && new_pat->action->folder) fs_give((void **)&new_pat->action->folder); else if(new_pat->action->is_a_filter && new_pat->action->kill == 0 && new_pat->action->folder == 0){ free_pat(&new_pat); Tcl_SetResult(interp, "No folder set for Move", TCL_VOLATILE); return(TCL_OK); } } if(aflags & RS_RULE_EDIT) rv = edit_pattern(new_pat, rno, rflags); else if(aflags & RS_RULE_ADD) rv = add_pattern(new_pat, rflags); else if(aflags & RS_RULE_DELETE) rv = delete_pattern(rno, rflags); else if(aflags & RS_RULE_SHUFFUP) rv = shuffle_pattern(rno, 1, rflags); else if(aflags & RS_RULE_SHUFFDOWN) rv = shuffle_pattern(rno, -1, rflags); else rv = 1; return(rv ? TCL_ERROR : TCL_OK); } #if 0 ADDRESS * peAEToAddress(AdrBk_Entry *ae) { char *list, *l1, *l2; int length; BuildTo bldto; ADDRESS *addr = NULL; if(ae->tag == List){ length = 0; for(l2 = ae->addr.list; *l2; l2++) length += (strlen(*l2) + 1); list = (char *) fs_get(length + 1); list[0] = '\0'; l1 = list; for(l2 = ae->addr.list; *l2; l2++){ if(l1 != list && l1-list < length+1) *l1++ = ','; strncpy(l1, *l2, length+1-(l1-list)); l1 += strlen(l1); } list[length] = '\0'; bldto.type = Str; bldto.arg.str = list; adr2 = expand_address(bldto, userdomain, localdomain, loop_detected, fcc, did_set, lcc, error, 1, simple_verify, mangled); fs_give((void **) &list); } else if(ae->tag == Single){ if(strucmp(ae->addr.addr, a->mailbox)){ bldto.type = Str; bldto.arg.str = ae->addr.addr; adr2 = expand_address(bldto, userdomain, localdomain, loop_detected, fcc, did_set, lcc, error, 1, simple_verify, mangled); } else{ /* * A loop within plain single entry is ignored. * Set up so later code thinks we expanded. */ adr2 = mail_newaddr(); adr2->mailbox = cpystr(ae->addr.addr); adr2->host = cpystr(userdomain); adr2->adl = cpystr(a->adl); } } /* * Personal names: If the expanded address has a personal * name and the address book entry is a list with a fullname, * tack the full name from the address book on in front. * This mainly occurs with a distribution list where the * list has a full name, and the first person in the list also * has a full name. * * This algorithm doesn't work very well if lists are * included within lists, but it's not clear what would * be better. */ if(ae->fullname && ae->fullname[0]){ if(adr2->personal && adr2->personal[0]){ if(ae->tag == List){ /* combine list name and existing name */ char *name; if(!simple_verify){ size_t l; l = strlen(adr2->personal) + strlen(ae->fullname) + 4; name = (char *)fs_get((l+1) * sizeof(char)); snprintf(name, l+1, "%s -- %s", ae->fullname, adr2->personal); fs_give((void **)&adr2->personal); adr2->personal = name; } } else{ /* replace with nickname fullname */ fs_give((void **)&adr2->personal); adr2->personal = adrbk_formatname(ae->fullname, NULL, NULL); } } else{ if(abe-p>tag != List || !simple_verify){ if(adr2->personal) fs_give((void **)&adr2->personal); adr2->personal = adrbk_formatname(abe->fullname, NULL, NULL); } } } return(addr); } char * peAEFcc(AdrBk_Entry *ae) { char *fcc = NULL; if(ae->fcc && ae->fcc[0]){ if(!strcmp(ae->fcc, "\"\"")) fcc = cpystr(""); else fcc = cpystr(ae->fcc); } else if(ae->nickname && ae->nickname[0] && (ps_global->fcc_rule == FCC_RULE_NICK || ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){ /* * else if fcc-rule=fcc-by-nickname, use that */ fcc = cpystr(ae->nickname); } return(fcc); } #endif PINEFIELD * peCustomHdrs(void) { extern PINEFIELD *parse_custom_hdrs(char **, CustomType); return(parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef)); } /* * PEClistCmd - Collection list editing tools */ int PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *err = "Unknown PEClist request"; dprint((2, "PEClistCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); } else{ char *s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(objc == 3){ /* delete */ if(!strcmp(s1, "delete")){ int cl, i, n, deln; char **newl; CONTEXT_S *del_ctxt, *tmp_ctxt, *new_ctxt; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){ Tcl_SetResult(interp, "cledit malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } for(i = 0, del_ctxt = ps_global->context_list; del_ctxt && i < cl; i++, del_ctxt = del_ctxt->next); if(!del_ctxt) return(TCL_ERROR); for(n = 0; del_ctxt->var.v->current_val.l[n]; n++); n--; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; deln = del_ctxt->var.i; for(i = 0; del_ctxt->var.v->current_val.l[i]; i++){ if(i < deln) newl[i] = cpystr(del_ctxt->var.v->current_val.l[i]); else if(i > deln) newl[i-1] = cpystr(del_ctxt->var.v->current_val.l[i]); } n = set_variable_list(del_ctxt->var.v - ps_global->vars, *newl ? newl : NULL, TRUE, Main); free_list_array(&newl); set_current_val(del_ctxt->var.v, TRUE, FALSE); if(n){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } for(tmp_ctxt = del_ctxt->next; tmp_ctxt && tmp_ctxt->var.v == del_ctxt->var.v; tmp_ctxt = tmp_ctxt->next) tmp_ctxt->var.i--; if((tmp_ctxt = del_ctxt->next) != NULL) tmp_ctxt->prev = del_ctxt->prev; if((tmp_ctxt = del_ctxt->prev) != NULL) tmp_ctxt->next= del_ctxt->next; if(!del_ctxt->prev && !del_ctxt->next){ new_ctxt = new_context(del_ctxt->var.v->current_val.l[0], NULL); ps_global->context_list = new_ctxt; if(!new_ctxt->var.v) new_ctxt->var = del_ctxt->var; } else if(ps_global->context_list == del_ctxt){ ps_global->context_list = del_ctxt->next; if(!ps_global->context_list) return TCL_ERROR; /* this shouldn't happen */ } if(ps_global->context_last == del_ctxt) ps_global->context_last = NULL; if(ps_global->context_current == del_ctxt){ strncpy(ps_global->cur_folder, ps_global->mail_stream->mailbox, sizeof(ps_global->cur_folder)); ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; ps_global->context_current = ps_global->context_list; } del_ctxt->prev = NULL; del_ctxt->next = NULL; free_context(&del_ctxt); init_inbox_mapping(ps_global->VAR_INBOX_PATH, ps_global->context_list); return TCL_OK; } else if(!strcmp(s1, "shuffdown")){ int cl, i, shn, n; CONTEXT_S *sh_ctxt, *nsh_ctxt, *tctxt; char **newl, *tmpch; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){ Tcl_SetResult(interp, "cledit malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } for(sh_ctxt = ps_global->context_list, i = 0; sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next); if(!sh_ctxt || !sh_ctxt->next){ Tcl_SetResult(interp, "invalid context list number", TCL_VOLATILE); return TCL_ERROR; } if(sh_ctxt->var.v == sh_ctxt->next->var.v){ shn = sh_ctxt->var.i; for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++); newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){ if(i == shn) newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]); else if(i == shn + 1) newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]); else newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]); } n = set_variable_list(sh_ctxt->var.v - ps_global->vars, newl, TRUE, Main); free_list_array(&newl); set_current_val(sh_ctxt->var.v, TRUE, FALSE); if(n){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } nsh_ctxt = sh_ctxt->next; nsh_ctxt->var.i--; sh_ctxt->var.i++; } else{ nsh_ctxt = sh_ctxt->next; shn = sh_ctxt->var.i; tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]); for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++); n--; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(i = 0; sh_ctxt->var.v->current_val.l[i+1]; i++) newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]); n = set_variable_list(sh_ctxt->var.v - ps_global->vars, newl, FALSE, Main); free_list_array(&newl); set_current_val(sh_ctxt->var.v, TRUE, FALSE); for(n = 0; nsh_ctxt->var.v->current_val.l[n]; n++); n++; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; newl[0] = cpystr(nsh_ctxt->var.v->current_val.l[0]); newl[1] = tmpch; for(i = 2; nsh_ctxt->var.v->current_val.l[i-1]; i++) newl[i] = cpystr(nsh_ctxt->var.v->current_val.l[i-1]); n = set_variable_list(nsh_ctxt->var.v - ps_global->vars, newl, TRUE, Main); free_list_array(&newl); set_current_val(nsh_ctxt->var.v, TRUE, FALSE); sh_ctxt->var.v = nsh_ctxt->var.v; sh_ctxt->var.i = 1; /* this for loop assumes that there are only two variable lists, * folder-collections and news-collections, a little more will * have to be done if we want to accommodate for the INHERIT * option introduced in 4.30. */ for(tctxt = nsh_ctxt->next; tctxt; tctxt = tctxt->next) tctxt->var.i++; } if(sh_ctxt->prev) sh_ctxt->prev->next = nsh_ctxt; nsh_ctxt->prev = sh_ctxt->prev; sh_ctxt->next = nsh_ctxt->next; nsh_ctxt->next = sh_ctxt; sh_ctxt->prev = nsh_ctxt; if(sh_ctxt->next) sh_ctxt->next->prev = sh_ctxt; if(ps_global->context_list == sh_ctxt) ps_global->context_list = nsh_ctxt; init_inbox_mapping(ps_global->VAR_INBOX_PATH, ps_global->context_list); return TCL_OK; } else if(!strcmp(s1, "shuffup")){ int cl, i, shn, n; CONTEXT_S *sh_ctxt, *psh_ctxt, *tctxt; char **newl, *tmpch; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){ Tcl_SetResult(interp, "cledit malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } for(sh_ctxt = ps_global->context_list, i = 0; sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next); if(!sh_ctxt || !sh_ctxt->prev){ Tcl_SetResult(interp, "invalid context list number", TCL_VOLATILE); return TCL_ERROR; } if(sh_ctxt->var.v == sh_ctxt->prev->var.v){ shn = sh_ctxt->var.i; for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++); newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){ if(i == shn) newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]); else if(i == shn - 1) newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]); else newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]); } i = set_variable_list(sh_ctxt->var.v - ps_global->vars, newl, TRUE, Main); free_list_array(&newl); set_current_val(sh_ctxt->var.v, TRUE, FALSE); if(i){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } psh_ctxt = sh_ctxt->prev; psh_ctxt->var.i++; sh_ctxt->var.i--; } else{ psh_ctxt = sh_ctxt->prev; shn = sh_ctxt->var.i; tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]); for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++); n--; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(i = 1; sh_ctxt->var.v->current_val.l[i]; i++) newl[i-1] = cpystr(sh_ctxt->var.v->current_val.l[i]); i = set_variable_list(sh_ctxt->var.v - ps_global->vars, newl, FALSE, Main); free_list_array(&newl); if(i){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } set_current_val(sh_ctxt->var.v, TRUE, FALSE); for(n = 0; psh_ctxt->var.v->current_val.l[n]; n++); n++; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(i = 0; psh_ctxt->var.v->current_val.l[i+1]; i++) newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i]); newl[i++] = tmpch; newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i-1]); i = set_variable_list(psh_ctxt->var.v - ps_global->vars, newl, TRUE, Main); free_list_array(&newl); if(i){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } set_current_val(psh_ctxt->var.v, TRUE, FALSE); for(tctxt = sh_ctxt->next ; tctxt; tctxt = tctxt->next) tctxt->var.i--; sh_ctxt->var.v = psh_ctxt->var.v; sh_ctxt->var.i = n - 2; /* There MUST be at least 2 collections in the list */ psh_ctxt->var.i++; } if(sh_ctxt->next) sh_ctxt->next->prev = psh_ctxt; psh_ctxt->next = sh_ctxt->next; sh_ctxt->prev = psh_ctxt->prev; psh_ctxt->prev = sh_ctxt; sh_ctxt->next = psh_ctxt; if(sh_ctxt->prev) sh_ctxt->prev->next = sh_ctxt; if(ps_global->context_list == psh_ctxt) ps_global->context_list = sh_ctxt; init_inbox_mapping(ps_global->VAR_INBOX_PATH, ps_global->context_list); return TCL_OK; } } else if(objc == 7){ if(!strcmp(s1, "edit") || !strcmp(s1, "add")){ int cl, quotes_needed = 0, i, add = 0, n = 0; char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4], **newl; CONTEXT_S *new_ctxt, *tmp_ctxt; if(!strcmp(s1, "add")) add = 1; if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){ Tcl_SetResult(interp, "cledit malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))){ Tcl_SetResult(interp, "Error1", TCL_VOLATILE); return TCL_ERROR; } if(!(server = Tcl_GetStringFromObj(objv[4], NULL))){ Tcl_SetResult(interp, "Error2", TCL_VOLATILE); return TCL_ERROR; } if(!(path = Tcl_GetStringFromObj(objv[5], NULL))){ Tcl_SetResult(interp, "Error3", TCL_VOLATILE); return TCL_ERROR; } if(!(view = Tcl_GetStringFromObj(objv[6], NULL))){ Tcl_SetResult(interp, "Error4", TCL_VOLATILE); return TCL_ERROR; } removing_leading_and_trailing_white_space(nick); removing_leading_and_trailing_white_space(server); removing_leading_and_trailing_white_space(path); removing_leading_and_trailing_white_space(view); if(strchr(nick, ' ')) quotes_needed = 1; if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) > MAILTMPLEN * 4 - 20) { /* for good measure */ Tcl_SetResult(interp, "info too long", TCL_VOLATILE); return TCL_ERROR; } if(3 + strlen(nick) + strlen(server) + strlen(path) + strlen(view) > MAILTMPLEN + 4){ Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE); return(TCL_OK); } snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ? "\"" : "", nick, quotes_needed ? "\"" : "", strlen(nick) ? " " : "", server, path, view); new_ctxt = new_context(context_buf, NULL); if(!add){ for(tmp_ctxt = ps_global->context_list, i = 0; tmp_ctxt && i < cl; i++, tmp_ctxt = tmp_ctxt->next); if(!tmp_ctxt){ Tcl_SetResult(interp, "invalid context list number", TCL_VOLATILE); return TCL_ERROR; } new_ctxt->next = tmp_ctxt->next; new_ctxt->prev = tmp_ctxt->prev; if(tmp_ctxt->prev && tmp_ctxt->prev->next == tmp_ctxt) tmp_ctxt->prev->next = new_ctxt; if(tmp_ctxt->next && tmp_ctxt->next->prev == tmp_ctxt) tmp_ctxt->next->prev = new_ctxt; if(ps_global->context_list == tmp_ctxt) ps_global->context_list = new_ctxt; if(ps_global->context_current == tmp_ctxt){ strncpy(ps_global->cur_folder, ps_global->mail_stream->mailbox, sizeof(ps_global->cur_folder)); ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; ps_global->context_current = new_ctxt; } if(ps_global->context_last == tmp_ctxt) ps_global->context_last = new_ctxt; new_ctxt->var = tmp_ctxt->var; tmp_ctxt->next = tmp_ctxt->prev = NULL; free_context(&tmp_ctxt); } else { for(tmp_ctxt = ps_global->context_list; tmp_ctxt->next; tmp_ctxt = tmp_ctxt->next); new_ctxt->prev = tmp_ctxt; tmp_ctxt->next = new_ctxt; new_ctxt->var.v = tmp_ctxt->var.v; new_ctxt->var.i = tmp_ctxt->var.i + 1; } if(!new_ctxt->var.v){ Tcl_SetResult(interp, "Error5", TCL_VOLATILE); return TCL_ERROR; } for(n = 0; new_ctxt->var.v->current_val.l[n]; n++); if(add) n++; newl = (char **) fs_get((n + 1) * sizeof(char *)); newl[n] = NULL; for(n = 0; new_ctxt->var.v->current_val.l[n]; n++) newl[n] = (n == new_ctxt->var.i) ? cpystr(context_buf) : cpystr(new_ctxt->var.v->current_val.l[n]); if(add) newl[n++] = cpystr(context_buf); n = set_variable_list(new_ctxt->var.v - ps_global->vars, newl, TRUE, Main); free_list_array(&newl); set_current_val(new_ctxt->var.v, TRUE, FALSE); init_inbox_mapping(ps_global->VAR_INBOX_PATH, ps_global->context_list); if(n){ Tcl_SetResult(interp, "Error saving changes", TCL_VOLATILE); return TCL_OK; } return TCL_OK; } } } } Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } /* * peTakeaddr - Take Address */ int peTakeaddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { TA_S *talist = NULL, *current, *head; Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL; int anum = 0; mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid)); if(set_up_takeaddr('a', ps_global, sp_msgmap(ps_global->mail_stream), &talist, &anum, TA_NOPROMPT, NULL) < 0 || (talist == NULL)){ Tcl_SetResult(interp, "Take address failed to set up", TCL_VOLATILE); return(TCL_ERROR); } for(head = talist ; head->prev; head = head->prev); /* * Return value will be of the form: * { * { "line to print", * {"personal", "mailbox", "host"} # addr * {"nick", "fullname", "fcc", "comment"} # suggested * } * ... * } * * The two list items will be empty if that line is * just informational. */ itemObj = Tcl_NewListObj(0, NULL); for(current = head; current ; current = current->next){ if(current->skip_it && !current->print) continue; secObj = Tcl_NewListObj(0, NULL); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(current->strvalue,-1)) != TCL_OK) return(TCL_ERROR); resObj = Tcl_NewListObj(0, NULL); /* append the address information */ if(current->addr && !current->print){ if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->addr->personal ? current->addr->personal : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->addr->mailbox ? current->addr->mailbox : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->addr->host ? current->addr->host : "", -1)) != TCL_OK) return(TCL_ERROR); } if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK) return(TCL_ERROR); resObj = Tcl_NewListObj(0, NULL); /* append the suggested possible entries */ if(!current->print && (current->nickname || current->fullname || current->fcc || current->comment)){ if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->nickname ? current->nickname : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->fullname ? current->fullname : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->fcc ? current->fcc : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(current->comment ? current->comment : "", -1)) != TCL_OK) return(TCL_ERROR); } if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj) != TCL_OK) return(TCL_ERROR); } free_talines(&talist); return(TCL_OK); } /* * peTakeFrom - Take only From Address */ int peTakeFrom(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv) { char *err = NULL; Tcl_Obj *objItem; ADDRESS *ap; ENVELOPE *env; long rawno; /* * Return value will be of the form: * { * { "line to print", * {"personal", "mailbox", "host"} # addr * {"nick", "fullname", "fcc", "comment"} # suggested * } * ... * } * * The two list items will be empty if that line is * just informational. */ if(uid){ if((env = pine_mail_fetchstructure(ps_global->mail_stream, rawno = peSequenceNumber(uid), NULL))){ /* append the address information */ for(ap = env->from; ap; ap = ap->next){ objItem = Tcl_NewListObj(0, NULL); /* append EMPTY "line to print" */ if(Tcl_ListObjAppendElement(interp, objItem, Tcl_NewStringObj("",-1)) != TCL_OK) return(TCL_ERROR); /* append address info */ peAppListF(interp, objItem, "%s%s%s", ap->personal ? (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, ap->personal) : "", ap->mailbox ? ap->mailbox : "", ap->host ? ap->host : ""); /* append suggested info */ peAddSuggestedContactInfo(interp, objItem, ap); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objItem) != TCL_OK) return(TCL_ERROR); } return(TCL_OK); } else err = ps_global->last_error; } else err = "Invalid UID"; return(TCL_ERROR); } int peAddSuggestedContactInfo(Tcl_Interp *interp, Tcl_Obj *lobjp, ADDRESS *addr) { char *nick = NULL, *full = NULL, *fcc = NULL, *comment = NULL; get_contactinfo_from_addr(addr, &nick, &full, &fcc, &comment); peAppListF(interp, lobjp, "%s%s%s%s", nick ? nick : "", full ? full : "", fcc ? fcc : "", comment ? comment : ""); if(nick) fs_give((void **) &nick); if(full) fs_give((void **) &full); if(fcc) fs_give((void **) &fcc); if(comment) fs_give((void **) &comment); return 0; } /* * * * * * * * * Status message ring management * * * * * * * * * * * * */ STATMSG_S * sml_newmsg(int priority, char *text) { static long id = 1; STATMSG_S *smp; smp = (STATMSG_S *) fs_get(sizeof(STATMSG_S)); memset(smp, 0, sizeof(STATMSG_S)); smp->id = id++; smp->posted = time(0); smp->type = priority; smp->text = cpystr(text); return(smp); } void sml_addmsg(int priority, char *text) { STATMSG_S *smp = sml_newmsg(priority, text); if(peStatList){ smp->next = peStatList; peStatList = smp; } else peStatList = smp; } char ** sml_getmsgs(void) { int n; STATMSG_S *smp; char **retstrs = NULL, **tmpstrs; for(n = 0, smp = peStatList; smp && !smp->seen; n++, smp = smp->next) ; if(n == 0) return NULL; retstrs = (char **)fs_get((n+1)*sizeof(char *)); for(tmpstrs = retstrs, smp = peStatList; smp && !smp->seen; smp = smp->next){ *tmpstrs = smp->text; tmpstrs++; } *tmpstrs = NULL; return(retstrs); } char * sml_getmsg(void) { return(peStatList ? peStatList->text : ""); } void sml_seen(void) { STATMSG_S *smp; for(smp = peStatList; smp; smp = smp->next) smp->seen = 1; } /* * * * * * * * * LDAP Support Routines * * * * * * * * * * * */ /* * PELdapCmd - LDAP TCL interface */ int PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { #ifndef ENABLE_LDAP char *err = "Call to PELdap when LDAP not enabled"; #else char *err = "Unknown PELdap request"; char *s1; dprint((2, "PELdapCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ int qn; if(!strcmp(s1, "directories")){ int i; LDAP_SERV_S *info; Tcl_Obj *secObj; if(objc != 2){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } if(ps_global->VAR_LDAP_SERVERS){ for(i = 0; ps_global->VAR_LDAP_SERVERS[i] && ps_global->VAR_LDAP_SERVERS[i][0]; i++){ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]); secObj = Tcl_NewListObj(0, NULL); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(info->nick ? info->nick : "", -1)) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(info->serv ? info->serv : "", -1)) != TCL_OK) return(TCL_ERROR); if(info) free_ldap_server_info(&info); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj) != TCL_OK) return(TCL_ERROR); } } else Tcl_SetResult(interp, "", TCL_STATIC); return(TCL_OK); } else if(!strcmp(s1, "query")){ int dir; char *srchstr, *filtstr; LDAP_CHOOSE_S *winning_e = NULL; LDAP_SERV_RES_S *results = NULL; WP_ERR_S wp_err; CUSTOM_FILT_S *filter = NULL; if(objc != 5){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[2], &dir) == TCL_ERROR){ Tcl_SetResult(interp, "PELdap results malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } wpldap_global->query_no++; if(wpldap_global->ldap_search_list){ wpldap_global->ldap_search_list = free_wpldapres(wpldap_global->ldap_search_list); } srchstr = Tcl_GetStringFromObj(objv[3], NULL); filtstr = Tcl_GetStringFromObj(objv[4], NULL); if(!srchstr) return(TCL_ERROR); if(!filtstr) return(TCL_ERROR); if(*filtstr){ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S)); filter->filt = cpystr(filtstr); filter->combine = 0; } memset(&wp_err, 0, sizeof(wp_err)); ldap_lookup_all(srchstr, dir, 0, AlwaysDisplay, filter, &winning_e, &wp_err, &results); if(filter){ fs_give((void **)&filter->filt); fs_give((void **)&filter); } Tcl_SetResult(interp, int2string(wpldap_global->ldap_search_list ? wpldap_global->query_no : 0), TCL_VOLATILE); return(TCL_OK); } /* * First argument has always got to be the query number for now. * Might need to rething that when setting up queries. */ if(objc == 2){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } if(Tcl_GetIntFromObj(interp, objv[2], &qn) == TCL_ERROR){ Tcl_SetResult(interp, "PELdap results malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } if(qn != wpldap_global->query_no){ Tcl_SetResult(interp, "Query is no longer valid", TCL_VOLATILE); return(TCL_ERROR); } if(objc == 3){ if(!strcmp(s1, "results")){ return(peLdapQueryResults(interp)); } } else if(objc == 4){ if(!strcmp(s1, "ldapext")){ /* * Returns a list of the form: * {"dn" {{attrib {val, ...}}, ...}} */ char *whichrec = Tcl_GetStringFromObj(objv[3], NULL); char *tmpstr, *tmp, *tmp2, *a; struct berval **vals; WPLDAPRES_S *curres; LDAP_CHOOSE_S *winning_e = NULL; LDAP_SERV_RES_S *trl; Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj; BerElement *ber; LDAPMessage *e; int i, j, whichi, whichj; if(whichrec == NULL){ Tcl_SetResult(interp, "Ldap ldapext error 1", TCL_VOLATILE); return TCL_ERROR; } tmpstr = cpystr(whichrec); tmp = tmpstr; for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != '.'){ Tcl_SetResult(interp, "Ldap ldapext error 2", TCL_VOLATILE); return TCL_ERROR; } *tmp2 = '\0'; whichi = atoi(tmp); *tmp2 = '.'; tmp2++; for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != '\0'){ Tcl_SetResult(interp, "Ldap ldapext error 3", TCL_VOLATILE); return TCL_ERROR; } whichj = atoi(tmp); fs_give((void **)&tmpstr); for(curres = wpldap_global->ldap_search_list, i = 0; i < whichi && curres; i++, curres = curres->next); if(!curres){ Tcl_SetResult(interp, "Ldap ldapext error 4", TCL_VOLATILE); return TCL_ERROR; } for(trl = curres->reslist, j = 0; trl; trl = trl->next){ for(e = ldap_first_entry(trl->ld, trl->res); e != NULL && j < whichj; e = ldap_next_entry(trl->ld, e), j++); if(e != NULL && j == whichj) break; } if(e == NULL || trl == NULL){ Tcl_SetResult(interp, "Ldap ldapext error 5", TCL_VOLATILE); return TCL_ERROR; } winning_e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S)); winning_e->ld = trl->ld; winning_e->selected_entry = e; winning_e->info_used = trl->info_used; winning_e->serv = trl->serv; a = ldap_get_dn(winning_e->ld, winning_e->selected_entry); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a ? a : "", -1)) != TCL_OK) return(TCL_ERROR); if(a) our_ldap_dn_memfree(a); itemObj = Tcl_NewListObj(0, NULL); for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber); a != NULL; a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){ if(a && *a){ secObj = Tcl_NewListObj(0, NULL); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(ldap_translate(a, winning_e->info_used), -1)) != TCL_OK) return(TCL_ERROR); resObj = Tcl_NewListObj(0, NULL); vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a); if(vals){ for(i = 0; vals[i]; i++){ if(Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(vals[i]->bv_val, -1)) != TCL_OK) return(TCL_ERROR); } ldap_value_free_len(vals); if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK) return(TCL_ERROR); } if(!strcmp(a,"objectclass")){ if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj("objectclass", -1)) != TCL_OK) return(TCL_ERROR); } if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK) return(TCL_ERROR); } our_ldap_memfree(a); } if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), itemObj) != TCL_OK) return(TCL_ERROR); fs_give((void **)&winning_e); return(TCL_OK); } } else if(objc == 6){ if(!strcmp(s1, "setaddrs")){ char *listset = Tcl_GetStringFromObj(objv[3], NULL); char *addrstr = Tcl_GetStringFromObj(objv[4], NULL); char *tmp, *tmp2, *tmplistset, was_char, *ret_to, *tmpaddrstr; int **lset, noreplace = 0; ADDRESS *adr = NULL, *curadr, *prevadr, *newadr, *curnewadr, *newadrs; int curi, i, j, numsrchs, numset, setit; LDAP_CHOOSE_S *tres; LDAP_SERV_RES_S *trl; WPLDAPRES_S *curres; LDAPMessage *e; RFC822BUFFER rbuf; size_t len; if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){ Tcl_SetResult(interp, "PELdap results malformed: first arg must be int", TCL_VOLATILE); return(TCL_ERROR); } if(listset == NULL || addrstr == NULL) return TCL_ERROR; tmpaddrstr = cpystr(addrstr); if(!noreplace){ mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr); rfc822_parse_adrlist(&adr, tmpaddrstr, "@"); mail_parameters(NIL, SET_PARSEPHRASE, NULL); } tmplistset = cpystr(listset); for(curres = wpldap_global->ldap_search_list, numsrchs = 0; curres; curres = curres->next, numsrchs++); lset = (int **)fs_get((numsrchs+1)*sizeof(int *)); for(i = 0; i < numsrchs; i++){ for(tmp = tmplistset, numset = 0; *tmp;){ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != '.'){ Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE); return TCL_ERROR; } if(atoi(tmp) == i) numset++; tmp2++; for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != ',' && *tmp2 != '\0'){ Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE); return TCL_ERROR; } if(*tmp2) tmp2++; tmp = tmp2; } lset[i] = (int *)fs_get((numset+1)*sizeof(int)); for(tmp = tmplistset, j = 0; *tmp && j < numset;){ setit = 0; for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != '.'){ Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE); return TCL_ERROR; } *tmp2 = '\0'; if(atoi(tmp) == i) setit++; *tmp2 = '.'; tmp2++; for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++); if(*tmp2 != ',' && *tmp2 != '\0'){ Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE); return TCL_ERROR; } if(setit){ was_char = *tmp2; *tmp2 = '\0'; lset[i][j++] = atoi(tmp); *tmp2 = was_char; } if(*tmp2) tmp2++; tmp = tmp2; } lset[i][j] = -1; } lset[i] = NULL; for(i = 0, curres = wpldap_global->ldap_search_list; i < numsrchs && curres; i++, curres = curres->next){ prevadr = NULL; for(curadr = adr; curadr; curadr = curadr->next){ if(strcmp(curadr->mailbox, curres->str) == 0 && curadr->host && *curadr->host == '@') break; prevadr = curadr; } if(!curadr && !noreplace){ Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE); return TCL_ERROR; } newadrs = newadr = curnewadr = NULL; for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){ for(e = ldap_first_entry(trl->ld, trl->res); e != NULL && lset[i][curi] != -1; e = ldap_next_entry(trl->ld, e), j++){ if(j == lset[i][curi]){ tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S)); tres->ld = trl->ld; tres->selected_entry = e; tres->info_used = trl->info_used; tres->serv = trl->serv; newadr = address_from_ldap(tres); fs_give((void **)&tres); if(newadrs == NULL){ newadrs = curnewadr = newadr; } else { curnewadr->next = newadr; curnewadr = newadr; } curi++; } } } if(newadrs == NULL || curnewadr == NULL){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname"); q_status_message(SM_ORDER, 0, 3, tmp_20k_buf); newadr = copyaddr(curadr); if(newadrs == NULL){ newadrs = curnewadr = newadr; } else { curnewadr->next = newadr; curnewadr = newadr; } } curnewadr->next = curadr ? curadr->next : NULL; if(curadr) curadr->next = NULL; if(curadr == adr) adr = newadrs; else{ prevadr->next = newadrs; if(curadr) mail_free_address(&curadr); } } len = est_size(adr); ret_to = (char *)fs_get(len * sizeof(char)); ret_to[0] = '\0'; strip_personal_quotes(adr); rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = ret_to; rbuf.cur = ret_to; rbuf.end = ret_to+len-1; rfc822_output_address_list(&rbuf, adr, 0L, NULL); *rbuf.cur = '\0'; Tcl_SetResult(interp, ret_to, TCL_VOLATILE); fs_give((void **)&ret_to); fs_give((void **)&tmpaddrstr); fs_give((void **)&tmplistset); for(i = 0; lset[i]; i++) fs_give((void **)&lset[i]); fs_give((void **)&lset); if(adr) mail_free_address(&adr); return(TCL_OK); } } } #endif /* ENABLE_LDAP */ Tcl_SetResult(interp, err, TCL_STATIC); return(TCL_ERROR); } #ifdef ENABLE_LDAP int peLdapQueryResults(Tcl_Interp *interp) { WPLDAPRES_S *tsl; Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj; LDAPMessage *e; LDAP_SERV_RES_S *trl; /* returned list will be of the form: * * { * {search-string * {name, {title, ...}, {unit, ...}, * {org, ...}, {email, ...}}, * ... * }, * ... * } */ for(tsl = wpldap_global->ldap_search_list; tsl; tsl = tsl->next){ secObj = Tcl_NewListObj(0, NULL); if(Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(tsl->str ? tsl->str : "", -1)) != TCL_OK) return(TCL_ERROR); resObj = Tcl_NewListObj(0, NULL); for(trl = tsl->reslist; trl; trl = trl->next){ for(e = ldap_first_entry(trl->ld, trl->res); e != NULL; e = ldap_next_entry(trl->ld, e)){ char *dn; struct berval **cn, **org, **unit, **title, **mail, **sn; dn = NULL; cn = org = title = unit = mail = sn = NULL; itemObj = Tcl_NewListObj(0, NULL); peLdapEntryParse(trl, e, &cn, &org, &unit, &title, &mail, &sn); if(cn){ if(Tcl_ListObjAppendElement(interp, itemObj, Tcl_NewStringObj(cn[0]->bv_val, -1)) != TCL_OK) return(TCL_ERROR); ldap_value_free_len(cn); } else if(sn){ if(Tcl_ListObjAppendElement(interp, itemObj, Tcl_NewStringObj(sn[0]->bv_val, -1)) != TCL_OK) return(TCL_ERROR); ldap_value_free_len(sn); } else{ dn = ldap_get_dn(trl->ld, e); if(dn && !dn[0]){ our_ldap_dn_memfree(dn); dn = NULL; } if(Tcl_ListObjAppendElement(interp, itemObj, Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK) return(TCL_ERROR); if(dn) our_ldap_dn_memfree(dn); } if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR) return(TCL_ERROR); if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR) return(TCL_ERROR); if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR) return(TCL_ERROR); if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK) return(TCL_ERROR); if(title) ldap_value_free_len(title); if(unit) ldap_value_free_len(unit); if(org) ldap_value_free_len(org); if(mail) ldap_value_free_len(mail); } } if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK) return(TCL_ERROR); if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj) != TCL_OK) return(TCL_ERROR); } return(TCL_OK); } int peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, struct berval **strl) { Tcl_Obj *strlObj; int i; strlObj = Tcl_NewListObj(0, NULL); if(strl){ for(i = 0; ALPINE_LDAP_usable(strl, i); i++){ if(Tcl_ListObjAppendElement(interp, strlObj, Tcl_NewStringObj(strl[i]->bv_val, -1)) != TCL_OK) return(TCL_ERROR); } } if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK) return(TCL_ERROR); return(TCL_OK); } int init_ldap_pname(struct pine *ps) { if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0] == '\0'){ char *pname; struct variable *vtmp; if(ps->maildomain && *ps->maildomain && ps->VAR_USER_ID && *ps->VAR_USER_ID){ pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain); if(pname){ vtmp = &ps->vars[V_PERSONAL_NAME]; if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0') || (vtmp->is_fixed && !vtmp->fixed_val.p)){ if(vtmp->fixed_val.p) fs_give((void **)&vtmp->fixed_val.p); vtmp->fixed_val.p = cpystr(pname); } else { if(vtmp->global_val.p) fs_give((void **)&vtmp->global_val.p); vtmp->global_val.p = cpystr(pname); } fs_give((void **)&pname); set_current_val(vtmp, FALSE, FALSE); } } } return 0; } #endif /* ENABLE_LDAP */ /* * Note: this is taken straight out of pico/composer.c * * strqchr - returns pointer to first non-quote-enclosed occurance of ch in * the given string. otherwise NULL. * s -- the string * ch -- the character we're looking for * q -- q tells us if we start out inside quotes on entry and is set * correctly on exit. * m -- max characters we'll check for ch (set to -1 for no check) */ char * strqchr(char *s, int ch, int *q, int m) { int quoted = (q) ? *q : 0; for(; s && *s && m != 0; s++, m--){ if(*s == '"'){ quoted = !quoted; if(q) *q = quoted; } if(!quoted && *s == ch) return(s); } return(NULL); } Tcl_Obj * wp_prune_folders(CONTEXT_S *ctxt, char *fcc, int cur_month, char *type, unsigned pr, int *ok, int moved_fldrs, Tcl_Interp *interp) { Tcl_Obj *resObj = NULL, *secObj = NULL; char path2[MAXPATH+1], tmp[21]; int exists, month_to_use; struct sm_folder *mail_list, *sm; mail_list = get_mail_list(ctxt, fcc); for(sm = mail_list; sm != NULL && sm->name != NULL; sm++) if(sm->month_num == cur_month - 1) break; /* matched a month */ month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0; if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){ strncpy(path2, fcc, sizeof(path2)-1); path2[sizeof(path2)-1] = '\0'; strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1); tmp[sizeof(tmp)-1] = '\0'; lcase((unsigned char *) tmp); snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12); if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){ (*ok) = 0; return(NULL); } else if(exists & FEX_ISFILE){ if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){ prune_move_folder(fcc, path2, ctxt); } else { resObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1)); secObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1)); Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1)); Tcl_ListObjAppendElement(interp, resObj, secObj); } } } if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK || pr == PRUNE_NO_AND_ASK){ sm = mail_list; if(!resObj && sm && sm->name && sm->name[0] != '\0'){ resObj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1)); Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL)); } if(resObj) secObj = Tcl_NewListObj(0, NULL); for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){ if(sm->name[0] == '\0') /* can't happen */ continue; Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1)); } if(resObj) Tcl_ListObjAppendElement(interp, resObj, secObj); } else if(resObj) Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL)); free_folder_list(ctxt); if((sm = mail_list) != NULL){ while(sm->name){ fs_give((void **)&(sm->name)); sm++; } fs_give((void **)&mail_list); } return(resObj); } int hex_colorstr(char *hexcolor, char *str) { char *tstr, *p, *p2, tbuf[256]; int i; strcpy(hexcolor, "000000"); tstr = color_to_asciirgb(str); p = tstr; p2 = strindex(p, ','); if(p2 == NULL) return 0; strncpy(tbuf, p, min(50, p2-p)); i = atoi(tbuf); sprintf(hexcolor, "%2.2x", i); p = p2+1; p2 = strindex(p, ','); if(p2 == NULL) return 0; strncpy(tbuf, p, min(50, p2-p)); i = atoi(tbuf); sprintf(hexcolor+2, "%2.2x", i); p = p2+1; strncpy(tbuf, p, 50); i = atoi(tbuf); sprintf(hexcolor+4, "%2.2x", i); return 0; } int hexval(char ch) { if(ch >= '0' && ch <= '9') return (ch - '0'); else if (ch >= 'A' && ch <= 'F') return (10 + (ch - 'A')); else if (ch >= 'a' && ch <= 'f') return (10 + (ch - 'a')); return -1; } int ascii_colorstr(char *acolor, char *hexcolor) { int i, hv; if(strlen(hexcolor) > 6) return 1; /* red value */ if((hv = hexval(hexcolor[0])) == -1) return 1; i = 16 * hv; if((hv = hexval(hexcolor[1])) == -1) return 1; i += hv; sprintf(acolor, "%3.3d,", i); /* green value */ if((hv = hexval(hexcolor[2])) == -1) return 1; i = 16 * hv; if((hv = hexval(hexcolor[3])) == -1) return 1; i += hv; sprintf(acolor+4, "%3.3d,", i); /* blue value */ if((hv = hexval(hexcolor[4])) == -1) return 1; i = 16 * hv; if((hv = hexval(hexcolor[5])) == -1) return 1; i += hv; sprintf(acolor+8, "%3.3d", i); return 0; } char * peRandomString(char *b, int l, int f) { static char *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; char *s = b; int j; long n; while(1){ n = random(); for(j = 0; j < ((sizeof(long) * 8) / 5); j++){ if(l-- <= 0){ *s = '\0'; return(b); } switch(f){ case PRS_LOWER_CASE : *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]); break; case PRS_MIXED_CASE : if(random() % 2){ *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]); break; } default : *s++ = kb[(n & 0x1F)]; break; } n = n >> 5; } } } long peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message) { char *t,*t1,tmp[MAILTMPLEN]; unsigned long u; MESSAGECACHE *elt; APPEND_PKG *ap = (APPEND_PKG *) data; *flags = *date = NIL; /* assume no flags or date */ if (ap->flags) fs_give ((void **) &ap->flags); if (ap->date) fs_give ((void **) &ap->date); mail_gc (ap->stream,GC_TEXTS); if (++ap->msgno <= ap->msgmax) { /* initialize flag string */ memset (t = tmp,0,MAILTMPLEN); /* output system flags */ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';} if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';} if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';} if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';} if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';} if ((u = elt->user_flags) != 0L) do /* any user flags? */ if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) { if(t-tmp < sizeof(tmp)) *t++ = ' '; /* space delimiter */ strncpy (t,t1,sizeof(tmp)-(t-tmp)); /* copy the user flag */ } while (u); /* until no more user flags */ tmp[sizeof(tmp)-1] = '\0'; *flags = ap->flags = cpystr (tmp + 1); *date = ap->date = cpystr (mail_date (tmp,elt)); *message = ap->message; /* message stringstruct */ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size); } else *message = NIL; /* all done */ return LONGT; } /* Initialize file string structure for file stringstruct * Accepts: string structure * pointer to message data structure * size of string */ void ms_init(STRING *s, void *data, unsigned long size) { APPEND_PKG *md = (APPEND_PKG *) data; s->data = data; /* note stream/msgno and header length */ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT); mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL); s->size += s->data1; /* header + body size */ SETPOS (s,0); } /* Get next character from file stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ char ms_next(STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for file stringstruct * Accepts: string structure * new position */ void ms_setpos(STRING *s, unsigned long i) { APPEND_PKG *md = (APPEND_PKG *) s->data; if (i < s->data1) { /* want header? */ s->chunk = mail_fetchheader (md->stream,md->msgno); s->chunksize = s->data1; /* header length */ s->offset = 0; /* offset is start of message */ } else if (i < s->size) { /* want body */ s->chunk = mail_fetchtext (md->stream,md->msgno); s->chunksize = s->size - s->data1; s->offset = s->data1; /* offset is end of header */ } else { /* off end of message */ s->chunk = NIL; /* make sure that we crack on this then */ s->chunksize = 1; /* make sure SNX cracks the right way... */ s->offset = i; } /* initial position and size */ s->curpos = s->chunk + (i -= s->offset); s->cursize = s->chunksize - i; } int remote_pinerc_failure(void) { snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s", ps_global->c_client_error[0] ? ps_global->c_client_error : _("Unable to read remote configuration")); return(TRUE); } char * peWebAlpinePrefix(void) { return("Web "); } void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){ char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1], *folder = NULL, intro[MAILTMPLEN+1]; long number; ENVELOPE *e = NULL; Tcl_Obj *resObj; if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream))); Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n))); if(stream){ e = pine_mail_fetchstructure(stream, n, NULL); if(sp_flagged(stream, SP_INBOX)) folder = NULL; else{ folder = STREAMNAME(stream); if(folder[0] == '?' && folder[1] == '\0') folder = NULL; } } format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro)); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%.80s%.80s", intro, from ? ((number > 1L) ? " Most recent f" : " F") : "", from ? "rom " : "", from ? from : "", subjtext); Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(tmp_20k_buf,-1)); Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj); } } /* * * * * * * * * RSS 2.0 Support Routines * * * * * * * * * * * */ /* * PERssCmd - RSS TCL interface */ int PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *s1; dprint((2, "PERssCmd")); if(objc == 1){ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?"); return(TCL_ERROR); } s1 = Tcl_GetStringFromObj(objv[1], NULL); if(s1){ if(!strcmp(s1, "news")){ return(peRssReturnFeed(interp, "news", ps_global->VAR_RSS_NEWS)); } else if(!strcmp(s1, "weather")){ return(peRssReturnFeed(interp, "weather", ps_global->VAR_RSS_WEATHER)); } } Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC); return(TCL_ERROR); } /* * peRssReturnFeed - fetch feed contents and package Tcl response */ int peRssReturnFeed(Tcl_Interp *interp, char *type, char *link) { RSS_FEED_S *feed; char *errstr = "UNKNOWN"; if(link){ ps_global->c_client_error[0] = '\0'; if((feed = peRssFeed(interp, type, link)) != NULL) return(peRssPackageFeed(interp, feed)); if(ps_global->mm_log_error) errstr = ps_global->c_client_error; } else errstr = "missing setting"; snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr); Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE); return(TCL_ERROR); } /* * peRssPackageFeed - build a list of feed item elements * * LIST ORDER: {title} {link} {description} {image} */ int peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed) { RSS_ITEM_S *item; for(item = feed->items; item; item = item->next) if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s", (item->title && *item->title)? item->title : "Feed Provided No Title", item->link ? item->link : "", item->description ? item->description : "", feed->image ? feed->image : "") != TCL_OK) return(TCL_ERROR); return(TCL_OK); } /* * peRssFeed - return cached feed struct or fetch a new one */ RSS_FEED_S * peRssFeed(Tcl_Interp *interp, char *type, char *link) { int i, cache_l, cp_ref; time_t now = time(0); RSS_FEED_S *feed = NULL; RSS_CACHE_S *cache, *cp; static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE]; if(!strucmp(type,"news")){ cache = &news_cache[0]; cache_l = RSS_NEWS_CACHE_SIZE; } else{ cache = &weather_cache[0]; cache_l = RSS_WEATHER_CACHE_SIZE; } /* search/purge cache */ for(i = 0; i < cache_l; i++) if(cache[i].link){ if(now > cache[i].stale){ peRssClearCacheEntry(&cache[i]); } else if(!strcmp(link, cache[i].link)){ cache[i].referenced++; return(cache[i].feed); /* HIT! */ } } if((feed = peRssFetch(interp, link)) != NULL){ /* find cache slot, and insert feed into cache */ for(i = 0, cp_ref = 0; i < cache_l; i++) if(!cache[i].feed){ cp = &cache[i]; break; } else if(cache[i].referenced >= cp_ref) cp = &cache[i]; if(!cp) cp = &cache[0]; /* failsafe */ peRssClearCacheEntry(cp); /* make sure */ cp->link = cpystr(link); cp->feed = feed; cp->referenced = 0; cp->stale = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60); } return(feed); } /* * peRssFetch - follow the provided link an return the resulting struct */ RSS_FEED_S * peRssFetch(Tcl_Interp *interp, char *link) { char *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL; char *buffer = NULL, *bp, *p, *q; int ttl = 60; unsigned long port = 0L, buffer_len = 0L; time_t theirdate = 0; STORE_S *feed_so = NULL; TCPSTREAM *tcp_stream; if(link){ /* grok url */ rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag); if(scheme && loc && path){ if((p = strchr(loc,':')) != NULL){ *p++ = '\0'; while(*p && isdigit((unsigned char) *p)) port = ((port * 10) + (*p++ - '0')); if(*p){ Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC); peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag); return(NULL); } } if(scheme && !strucmp(scheme, "feed")){ fs_give((void **) &scheme); scheme = cpystr("http"); } mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5); tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT); mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30); if(tcp_stream != NULL){ char rev[128]; snprintf(tmp_20k_buf, SIZEOF_20KBUF, "GET /%s%s%s%s%s HTTP/1.1\r\nHost: %s\r\nAccept: application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nUser-Agent: Web-Alpine/%s (%s %s)\r\n\r\n", path, parms ? ":" : "", parms ? parms : "", query ? "?" : "", query ? query : "", loc, ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev))); mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5); mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5); if(tcp_sout(tcp_stream, tmp_20k_buf, strlen(tmp_20k_buf))){ int ok = 0, chunked = FALSE; while((p = tcp_getline(tcp_stream)) != NULL){ if(!ok){ ok++; if(strucmp(p,"HTTP/1.1 200 OK")){ fs_give((void **) &p); break; /* bail */ } } else if(*p == '\0'){ /* first blank line, start of body */ if(buffer || feed_so){ fs_give((void **) &p); break; /* bail */ } if(buffer_len){ buffer = fs_get(buffer_len + 16); if(!tcp_getbuffer(tcp_stream, buffer_len, buffer)) fs_give((void **) &buffer); fs_give((void **) &p); break; /* bail */ } else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){ fs_give((void **) &p); break; /* bail */ } } else if(feed_so){ /* collect body */ if(chunked){ int chunk_len = 0, gotbuf; /* first line is chunk size in hex */ for(q = p; *q && isxdigit((unsigned char) *q); q++) chunk_len = (chunk_len * 16) + XDIGIT2C(*q); if(chunk_len > 0){ /* collect chunk */ char *tbuf = fs_get(chunk_len + 16); gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf); if(gotbuf) so_nputs(feed_so, tbuf, chunk_len); fs_give((void **) &tbuf); if(!gotbuf){ fs_give((void **) &p); break; /* bail */ } } /* collect trailing CRLF */ gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0'); if(q) fs_give((void **) &q); if(chunk_len == 0 || !gotbuf){ fs_give((void **) &p); break; /* bail */ } } else so_puts(feed_so, p); } else{ /* in header, grok fields */ if((q = strchr(p,':')) != NULL){ int l = q - p; *q++ = '\0'; while(isspace((unsigned char ) *q)) q++; /* content-length */ if(l == 4 && !strucmp(p, "date")){ theirdate = date_to_local_time_t(q); } else if(l == 7 && !strucmp(p, "expires")){ time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0)); if(expires > 0 && expires < (8 * 60 * 60)) ttl = expires; } else if(l == 12 && !strucmp(p, "content-type") && struncmp(q,"text/xml", 8) && struncmp(q,"application/xhtml+xml", 21) && struncmp(q,"application/rss+xml", 19) && struncmp(q,"application/xml", 15)){ fs_give((void **) &p); break; /* bail */ } else if(l == 13 && !strucmp(p, "cache-control")){ if(!struncmp(q,"max-age=",8)){ int secs = 0; for(q += 8; *q && isdigit((unsigned char) *q); q++) secs = ((secs * 10) + (*q - '0')); if(secs > 0) ttl = secs; } } else if(l == 14 && !strucmp(p,"content-length")){ while(*q && isdigit((unsigned char) *q)) buffer_len = ((buffer_len * 10) + (*q++ - '0')); if(*q){ fs_give((void **) &p); break; /* bail */ } } else if(l == 17 && !strucmp(p, "transfer-encoding")){ if(!struncmp(q,"chunked", 7)){ chunked = TRUE; } else{ /* unknown encoding */ fs_give((void **) &p); break; /* bail */ } } } } fs_give((void **) &p); } } else{ Tcl_SetResult(interp, "RSS send failure", TCL_STATIC); peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag); } tcp_close(tcp_stream); mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60); mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60); peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag); if(feed_so){ buffer = (char *) so_text(feed_so); buffer_len = (int) so_tell(feed_so); } if(buffer && buffer_len){ RSS_FEED_S *feed; char *err; STORE_S *bucket; gf_io_t gc, pc; /* grok response */ bucket = so_get(CharStar, NULL, EDIT_ACCESS); gf_set_readc(&gc, buffer, buffer_len, CharStar, 0); gf_set_so_writec(&pc, bucket); gf_filter_init(); gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0)); if((err = gf_pipe(gc, pc)) != NULL){ gf_html2plain_rss_free(&feed); Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC); } so_give(&bucket); if(feed_so) so_give(&feed_so); else fs_give((void **) &buffer); return(feed); } else Tcl_SetResult(interp, "RSS response error", TCL_STATIC); } else Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC); } else Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC); } else Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC); return(NULL); } void peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag) { if(scheme) fs_give((void **) scheme); if(loc) fs_give((void **) loc); if(path) fs_give((void **) path); if(parms) fs_give((void **) parms); if(query) fs_give((void **) query); if(frag) fs_give((void **) frag); } void peRssClearCacheEntry(RSS_CACHE_S *entry) { if(entry){ if(entry->link) fs_give((void **) &entry->link); gf_html2plain_rss_free(&entry->feed); memset(entry, 0, sizeof(RSS_CACHE_S)); } }