1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: alpined.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
4 
5 /* ========================================================================
6  * Copyright 2006-2008 University of Washington
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * ========================================================================
15  */
16 
17 /* ========================================================================
18     Implement alpine TCL interfaces.  Execute TCL interfaces
19     via interpreter reading commands and writing results over
20     UNIX domain socket.
21    ======================================================================== */
22 
23 
24 #include <system.h>
25 #include <general.h>
26 
27 #include "../../../c-client/c-client.h"
28 #include "../../../c-client/imap4r1.h"
29 
30 #include "../../../pith/osdep/color.h"	/* color support library */
31 #include "../../../pith/osdep/canaccess.h"
32 #include "../../../pith/osdep/temp_nam.h"
33 #include "../../../pith/osdep/collate.h"
34 #include "../../../pith/osdep/filesize.h"
35 #include "../../../pith/osdep/writ_dir.h"
36 #include "../../../pith/osdep/err_desc.h"
37 
38 #include "../../../pith/stream.h"
39 #include "../../../pith/context.h"
40 #include "../../../pith/state.h"
41 #include "../../../pith/msgno.h"
42 #include "../../../pith/debug.h"
43 #include "../../../pith/init.h"
44 #include "../../../pith/conf.h"
45 #include "../../../pith/conftype.h"
46 #include "../../../pith/detoken.h"
47 #include "../../../pith/flag.h"
48 #include "../../../pith/help.h"
49 #include "../../../pith/remote.h"
50 #include "../../../pith/status.h"
51 #include "../../../pith/mailcmd.h"
52 #include "../../../pith/savetype.h"
53 #include "../../../pith/save.h"
54 #include "../../../pith/reply.h"
55 #include "../../../pith/sort.h"
56 #include "../../../pith/ldap.h"
57 #include "../../../pith/addrbook.h"
58 #include "../../../pith/ablookup.h"
59 #include "../../../pith/takeaddr.h"
60 #include "../../../pith/bldaddr.h"
61 #include "../../../pith/copyaddr.h"
62 #include "../../../pith/thread.h"
63 #include "../../../pith/folder.h"
64 #include "../../../pith/mailview.h"
65 #include "../../../pith/indxtype.h"
66 #include "../../../pith/icache.h"
67 #include "../../../pith/mailindx.h"
68 #include "../../../pith/mailpart.h"
69 #include "../../../pith/mimedesc.h"
70 #include "../../../pith/detach.h"
71 #include "../../../pith/newmail.h"
72 #include "../../../pith/charset.h"
73 #include "../../../pith/util.h"
74 #include "../../../pith/rfc2231.h"
75 #include "../../../pith/string.h"
76 #include "../../../pith/send.h"
77 #include "../../../pith/options.h"
78 #include "../../../pith/list.h"
79 #include "../../../pith/mimetype.h"
80 #include "../../../pith/mailcap.h"
81 #include "../../../pith/sequence.h"
82 #include "../../../pith/smime.h"
83 #include "../../../pith/url.h"
84 #include "../../../pith/charconv/utf8.h"
85 
86 #include "alpined.h"
87 #include "color.h"
88 #include "imap.h"
89 #include "ldap.h"
90 #include "debug.h"
91 #include "stubs.h"
92 
93 #include <tcl.h>
94 
95 
96 /*
97  * Fake screen dimension for word wrap and such
98  */
99 #define	FAKE_SCREEN_WIDTH	80
100 #define	FAKE_SCREEN_LENGTH	24
101 
102 /*
103  * Arbitrary minimum display width (in characters)
104  */
105 #define	MIN_SCREEN_COLS		20
106 
107 
108 /*
109  * Maximum number of lines allowed in signatures
110  */
111 #define	SIG_MAX_LINES		24
112 #define	SIG_MAX_COLS		1024
113 
114 
115 /*
116  * Number of seconds we'll wait before we assume the client has wondered
117  * on to more interesting content
118  */
119 #define	PE_INPUT_TIMEOUT	1800
120 
121 
122 /*
123  * Posting error length max
124  */
125 #define	WP_MAX_POST_ERROR	128
126 
127 
128 /*
129  * AUTH Response Tokens
130  */
131 #define	AUTH_EMPTY_STRING	"NOPASSWD"
132 #define	AUTH_FAILURE_STRING	"BADPASSWD"
133 
134 /*
135  * CERT Response Tokens
136  */
137 #define	CERT_QUERY_STRING	"CERTQUERY"
138 #define	CERT_FAILURE_STRING	"CERTFAIL"
139 
140 
141 /*
142  * Charset used within alpined and to communicate with alpined
143  * Note: posting-charset still respected
144  */
145 #define	WP_INTERNAL_CHARSET	"UTF-8"
146 
147 
148 /*
149  * Globals referenced throughout pine...
150  */
151 struct pine *wps_global;				/* THE global variable! */
152 
153 
154 /*
155  * More global state
156  */
157 long	   gPeITop, gPeICount;
158 
159 long	   gPeInputTimeout = PE_INPUT_TIMEOUT;
160 long	   gPEAbandonTimeout = 0;
161 
162 
163 /*
164  * Authorization issues
165  */
166 int	   peNoPassword, peCredentialError;
167 int	   peCertFailure, peCertQuery;
168 char	   peCredentialRequestor[CRED_REQ_SIZE];
169 
170 char	  *peSocketName;
171 
172 char	  **peTSig;
173 
174 CONTEXT_S *config_context_list;
175 
176 STRLIST_S *peCertHosts;
177 
178 bitmap_t changed_feature_list;
179 #define F_CH_ON(feature)	(bitnset((feature),changed_feature_list))
180 #define F_CH_OFF(feature)	(!F_CH_ON(feature))
181 #define F_CH_TURN_ON(feature)   (setbitn((feature),changed_feature_list))
182 #define F_CH_TURN_OFF(feature)  (clrbitn((feature),changed_feature_list))
183 #define F_CH_SET(feature,value) ((value) ? F_CH_TURN_ON((feature))       \
184 					 : F_CH_TURN_OFF((feature)))
185 
186 
187 typedef struct _status_msg {
188     time_t		posted;
189     unsigned	        type:3;
190     unsigned	        seen:1;
191     long	        id;
192     char	       *text;
193     struct _status_msg *next;
194 } STATMSG_S;
195 
196 static STATMSG_S *peStatList;
197 
198 typedef struct _composer_attachment {
199     unsigned		 file:1;
200     unsigned		 body:1;
201     char		*id;
202     union {
203 	struct {
204 	    char        *local;
205 	    char	*remote;
206 	    char	*type;
207 	    char	*subtype;
208 	    char	*description;
209 	    long	 size;
210 	} f;
211 	struct {
212 	    BODY	*body;
213 	} b;
214 	struct {
215 	    long  msgno;
216 	    char *part;
217 	} msg;
218     } l;
219     struct _composer_attachment *next;
220 } COMPATT_S;
221 
222 static COMPATT_S *peCompAttach;
223 
224 /*
225  * Holds data passed
226  */
227 typedef struct _msg_data {
228 	ENVELOPE  *outgoing;
229 	METAENV	  *metaenv;
230 	PINEFIELD *custom;
231 	STORE_S   *msgtext;
232 	STRLIST_S *attach;
233 	char	  *fcc;
234 	int	   fcc_colid;
235 	int	   postop_fcc_no_attach;
236 	char	  *charset;
237 	char	  *priority;
238 	int	 (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *);
239 	unsigned   flowed:1;
240 	unsigned   html:1;
241 	unsigned   qualified_addrs:1;
242 } MSG_COL_S;
243 
244 
245 /*
246  * locally global structure to keep track of various bits of state
247  * needed to collect filtered output
248  */
249 static struct _embedded_data {
250     Tcl_Interp *interp;
251     Tcl_Obj    *obj;
252     STORE_S    *store;
253     long	uid;
254     HANDLE_S   *handles;
255     char	inhandle;
256     ENVELOPE   *env;
257     BODY       *body;
258     struct {
259 	char	fg[7];
260 	char	bg[7];
261 	char	fgdef[7];
262 	char	bgdef[7];
263     } color;
264 } peED;
265 
266 
267 /*
268  * RSS stream cache
269  */
270 typedef struct _rss_cache_s {
271 	char	   *link;
272 	time_t	    stale;
273 	int	    referenced;
274 	RSS_FEED_S *feed;
275 } RSS_CACHE_S;
276 
277 #define	RSS_NEWS_CACHE_SIZE	1
278 #define	RSS_WEATHER_CACHE_SIZE	1
279 
280 
281 #ifdef ENABLE_LDAP
282 WPLDAP_S *wpldap_global;
283 #endif
284 
285 /*
286  * random string generator flags
287  */
288 #define	PRS_NONE	0x0000
289 #define	PRS_LOWER_CASE	0x0001
290 #define	PRS_UPPER_CASE	0x0002
291 #define	PRS_MIXED_CASE	0x0004
292 
293 /*
294  * peSaveWork flag definitions
295  */
296 #define	PSW_NONE	0x00
297 #define	PSW_COPY	0x01
298 #define	PSW_MOVE	0x02
299 
300 /*
301  * Message Collector flags
302  */
303 #define	PMC_NONE	0x00
304 #define	PMC_FORCE_QUAL	0x01
305 #define	PMC_PRSRV_ATT	0x02
306 
307 /*
308  * length of thread info string
309  */
310 #define	WP_MAX_THRD_S	64
311 
312 /*
313  * static buf size for putenv() if necessary
314  */
315 #define PUTENV_MAX	64
316 
317 
318 
319 /*----------------------------------------------------------------------
320   General use big buffer. It is used in the following places:
321     compose_mail:    while parsing header of postponed message
322     append_message2: while writing header into folder
323     q_status_messageX: while doing printf formatting
324     addr_book: Used to return expanded address in. (Can only use here
325                because mm_log doesn't q_status on PARSE errors !)
326     alpine.c: When address specified on command line
327     init.c: When expanding variable values
328     and many many more...
329 
330  ----*/
331 char         wtmp_20k_buf[20480];
332 
333 
334 
335 
336 /* Internal prototypes */
337 void	     peReturn(int, char *, const char *);
338 int	     peWrite(int, char *);
339 char	    *peCreateUserContext(Tcl_Interp *, char *, char *, char *);
340 void	     peDestroyUserContext(struct pine **);
341 char	    *peLoadConfig(struct pine *);
342 int	     peCreateStream(Tcl_Interp *, CONTEXT_S *, char *, int);
343 void	     peDestroyStream(struct pine *);
344 void	     pePrepareForAuthException(void);
345 char	    *peAuthException(void);
346 void	     peInitVars(struct pine *);
347 int	     peSelect(Tcl_Interp *, int, Tcl_Obj **, int);
348 int	     peSelectNumber(Tcl_Interp *, int, Tcl_Obj **, int);
349 int	     peSelectDate(Tcl_Interp *, int, Tcl_Obj **, int);
350 int	     peSelectText(Tcl_Interp *, int, Tcl_Obj **, int);
351 int	     peSelectStatus(Tcl_Interp *, int, Tcl_Obj **, int);
352 char	    *peSelValTense(Tcl_Obj *);
353 char	    *peSelValYear(Tcl_Obj *);
354 char	    *peSelValMonth(Tcl_Obj *);
355 char	    *peSelValDay(Tcl_Obj *);
356 int	     peSelValCase(Tcl_Obj *);
357 int	     peSelValField(Tcl_Obj *);
358 int	     peSelValFlag(Tcl_Obj *);
359 int	     peSelected(Tcl_Interp *, int, Tcl_Obj **, int);
360 int	     peSelectError(Tcl_Interp *, char *);
361 int	     peApply(Tcl_Interp *, int, Tcl_Obj **);
362 char	    *peApplyFlag(MAILSTREAM *, MSGNO_S *, char, int, long *);
363 int	     peApplyError(Tcl_Interp *, char *);
364 int	     peIndexFormat(Tcl_Interp *);
365 int	     peAppendIndexParts(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
366 int	     peAppendIndexColor(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
367 int	     peMessageStatusBits(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
368 char	    *peMsgStatBitString(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
369 Tcl_Obj	    *peMsgStatNameList(Tcl_Interp *, struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
370 int	     peNewMailResult(Tcl_Interp *);
371 int	     peMessageSize(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
372 int	     peMessageDate(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
373 int	     peMessageSubject(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
374 int	     peMessageFromAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
375 int	     peMessageToAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
376 int	     peMessageCcAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
377 int	     peMessageField(Tcl_Interp *, imapuid_t, char *);
378 int	     peMessageStatus(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
379 int	     peMessageCharset(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
380 int	     peMessageBounce(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
381 int	     peMessageSpamNotice(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
382 char	    *peSendSpamReport(long, char *, char *, char *);
383 int	     peMsgnoFromUID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
384 int	     peMessageText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
385 int	     peMessageHeader(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
386 void	     peFormatEnvelope(MAILSTREAM *, long, char *, ENVELOPE *, gf_io_t, long, char *, int);
387 void	     peFormatEnvelopeAddress(MAILSTREAM *, long, char *, char *, ADDRESS *, int, char *, gf_io_t);
388 void	     peFormatEnvelopeNewsgroups(char *, char *, int, gf_io_t);
389 void	     peFormatEnvelopeText(char *, char *);
390 int	     peMessageAttachments(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
391 int	     peMessageBody(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
392 int	     peMessagePartFromCID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
393 int	     peLocateBodyByCID(char *, char *, BODY *);
394 char	    *peColorStr(char *, char *);
395 int	     peInterpWritec(int);
396 int	     peInterpFlush(void);
397 int	     peNullWritec(int);
398 void	     peGetMimeTyping(BODY *, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **);
399 int	     peGetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
400 int	     peIsFlagged(MAILSTREAM *, imapuid_t, char *);
401 int	     peSetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
402 int	     peMsgSelect(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
403 int	     peReplyHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
404 int	     peAppListF(Tcl_Interp *, Tcl_Obj *, char *, ...);
405 void	     pePatAppendID(Tcl_Interp *, Tcl_Obj *, PAT_S *);
406 void	     pePatAppendPattern(Tcl_Interp *, Tcl_Obj *, PAT_S *);
407 char	    *pePatStatStr(int);
408 int	     peReplyText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
409 int	     peSoStrToList(Tcl_Interp *, Tcl_Obj *, STORE_S *);
410 int	     peForwardHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
411 int	     peForwardText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
412 int	     peDetach(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
413 int	     peAttachInfo(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
414 int	     peSaveDefault(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
415 int	     peSaveWork(Tcl_Interp *, imapuid_t, int, Tcl_Obj **, long);
416 int	     peSave(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
417 int	     peCopy(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
418 int	     peMove(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
419 int	     peGotoDefault(Tcl_Interp *, imapuid_t, Tcl_Obj **);
420 int	     peTakeaddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
421 int	     peTakeFrom(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
422 int	     peAddSuggestedContactInfo(Tcl_Interp *, Tcl_Obj *, ADDRESS *);
423 int	     peReplyQuote(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
424 long	     peMessageNumber(imapuid_t);
425 long	     peSequenceNumber(imapuid_t);
426 int	     peMsgCollector(Tcl_Interp *, int, Tcl_Obj **,
427 			    int (*)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long);
428 int	     peMsgCollected(Tcl_Interp  *, MSG_COL_S *, char *, long);
429 void	     peMsgSetParm(PARAMETER **, char *, char *);
430 Tcl_Obj	    *peMsgAttachCollector(Tcl_Interp *, BODY *);
431 int	     peFccAppend(Tcl_Interp *, Tcl_Obj *, char *, int);
432 int	     peDoPost(METAENV *, BODY *, char *, CONTEXT_S **, char *);
433 int	     peDoPostpone(METAENV *, BODY *, char *, CONTEXT_S **, char *);
434 int	     peWriteSig (Tcl_Interp *, char *, Tcl_Obj **);
435 int	     peInitAddrbooks(Tcl_Interp *, int);
436 int	     peRuleStatVal(char *, int *);
437 int	     peRuleSet(Tcl_Interp *, Tcl_Obj **);
438 int          peAppendCurrentSort(Tcl_Interp *interp);
439 int          peAppendDefaultSort(Tcl_Interp *interp);
440 #if	0
441 ADDRESS	    *peAEToAddress(AdrBk_Entry *);
442 char	    *peAEFcc(AdrBk_Entry *);
443 #endif
444 NAMEVAL_S   *sort_key_rules(int);
445 NAMEVAL_S   *wp_indexheight_rules(int);
446 PINEFIELD   *peCustomHdrs(void);
447 STATMSG_S   *sml_newmsg(int, char *);
448 char	    *sml_getmsg(void);
449 char	   **sml_getmsgs(void);
450 void	     sml_seen(void);
451 #ifdef ENABLE_LDAP
452 int	     peLdapQueryResults(Tcl_Interp *);
453 int          peLdapStrlist(Tcl_Interp *, Tcl_Obj *, struct berval **);
454 int          init_ldap_pname(struct pine *);
455 #endif /* ENABLE_LDAP */
456 char	    *strqchr(char *, int, int *, int);
457 Tcl_Obj     *wp_prune_folders(CONTEXT_S *, char *, int, char *,
458 			      unsigned, int *, int, Tcl_Interp *);
459 int          hex_colorstr(char *, char *);
460 int          hexval(char);
461 int          ascii_colorstr(char *, char *);
462 COMPATT_S   *peNewAttach(void);
463 void	     peFreeAttach(COMPATT_S **);
464 COMPATT_S   *peGetAttachID(char *);
465 char	    *peFileAttachID(char *, char *, char *, char *, char *, int);
466 char	    *peBodyAttachID(BODY *);
467 void	     peBodyMoveContents(BODY *, BODY *);
468 int	     peClearAttachID(char *);
469 char	    *peRandomString(char *, int, int);
470 void	     ms_init(STRING *, void *, unsigned long);
471 char	     ms_next(STRING *);
472 void	     ms_setpos(STRING *, unsigned long);
473 long	     peAppendMsg(MAILSTREAM *, void *, char **, char **, STRING **);
474 int	     remote_pinerc_failure(void);
475 char	    *peWebAlpinePrefix(void);
476 void	     peNewMailAnnounce(MAILSTREAM *, long, long);
477 int	     peMessageNeedPassphrase(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
478 int	     peRssReturnFeed(Tcl_Interp *, char *, char *);
479 int	     peRssPackageFeed(Tcl_Interp *, RSS_FEED_S *);
480 RSS_FEED_S  *peRssFeed(Tcl_Interp *, char *, char *);
481 RSS_FEED_S  *peRssFetch(Tcl_Interp *, char *);
482 void	     peRssComponentFree(char **,char **,char **,char **,char **,char **);
483 void	     peRssClearCacheEntry(RSS_CACHE_S *);
484 
485 
486 /* Prototypes for Tcl-exported methods */
487 int	PEInit(Tcl_Interp *interp, char *);
488 void    PEExitCleanup(ClientData);
489 int	PEInfoCmd(ClientData clientData, Tcl_Interp *interp,
490 		  int objc, Tcl_Obj *CONST objv[]);
491 int	PEConfigCmd(ClientData clientData, Tcl_Interp *interp,
492 		    int objc, Tcl_Obj *CONST objv[]);
493 int	PEDebugCmd(ClientData clientData, Tcl_Interp *interp,
494 		   int objc, Tcl_Obj *CONST objv[]);
495 int	PESessionCmd(ClientData clientData, Tcl_Interp *interp,
496 		     int objc, Tcl_Obj *CONST objv[]);
497 int	PEMailboxCmd(ClientData clientData, Tcl_Interp *interp,
498 		     int objc, Tcl_Obj *CONST objv[]);
499 int	PEThreadCmd(ClientData clientData, Tcl_Interp *interp,
500 		    int objc, Tcl_Obj *CONST objv[]);
501 int	PEMessageCmd(ClientData clientData, Tcl_Interp *interp,
502 		     int objc, Tcl_Obj *CONST objv[]);
503 int	PEFolderCmd(ClientData clientData, Tcl_Interp *interp,
504 		    int objc, Tcl_Obj *CONST objv[]);
505 int	PEComposeCmd(ClientData clientData, Tcl_Interp *interp,
506 		     int objc, Tcl_Obj *CONST objv[]);
507 int	PEPostponeCmd(ClientData clientData, Tcl_Interp *interp,
508 		      int objc, Tcl_Obj *CONST objv[]);
509 int	PEAddressCmd(ClientData clientData, Tcl_Interp *interp,
510 		     int objc, Tcl_Obj *CONST objv[]);
511 int	PEClistCmd(ClientData clientData, Tcl_Interp *interp,
512 		   int objc, Tcl_Obj *CONST objv[]);
513 int	PELdapCmd(ClientData clientData, Tcl_Interp *interp,
514 		  int objc, Tcl_Obj *CONST objv[]);
515 int	PERssCmd(ClientData clientData, Tcl_Interp *interp,
516 		 int objc, Tcl_Obj *CONST objv[]);
517 
518 /* Append package */
519 typedef struct append_pkg {
520   MAILSTREAM *stream;		/* source stream */
521   unsigned long msgno;		/* current message number */
522   unsigned long msgmax;		/* maximum message number */
523   char *flags;			/* current flags */
524   char *date;			/* message internal date */
525   STRING *message;		/* stringstruct of message */
526 } APPEND_PKG;
527 
528 STRINGDRIVER mstring = {
529   ms_init,			/* initialize string structure */
530   ms_next,			/* get next byte in string structure */
531   ms_setpos			/* set position in string structure */
532 };
533 
534 
535 /*----------------------------------------------------------------------
536      main routine -- entry point
537 
538   Args: argv, argc -- The command line arguments
539 
540 
541   Setup c-client drivers and dive into TCL interpreter engine
542 
543   ----*/
544 
545 int
main(int argc,char * argv[])546 main(int argc, char *argv[])
547 {
548     int	   ev = 1, s, cs, n, co, o, l, bl = 256, argerr;
549     char   *buf, sname[256];
550     struct sockaddr_un name;
551     Tcl_Interp *interp;
552 #if	PUBCOOKIE
553     extern AUTHENTICATOR auth_gss_proxy;
554 #endif
555 
556     srandom(getpid() + time(0));
557 
558     /*----------------------------------------------------------------------
559            Initialize c-client
560       ----------------------------------------------------------------------*/
561 
562     /*
563      * NO LOCAL DRIVERS ALLOWED
564      * For this to change pintecld *MUST* be running under the user's UID and
565      * and signal.[ch] need to get fixed to handle KOD rather than change
566      * the debug level
567      */
568     mail_link (&imapdriver);		/* link in the imap driver */
569     mail_link (&unixdriver);		/* link in the unix driver */
570     mail_link (&dummydriver);		/* link in the dummy driver */
571 
572     /* link authentication drivers */
573 #if	PUBCOOKIE
574     auth_link (&auth_gss_proxy);	/* pubcoookie proxy authenticator */
575 #endif
576     auth_link (&auth_md5);		/* link in the md5 authenticator */
577     auth_link (&auth_pla);
578     auth_link (&auth_log);		/* link in the log authenticator */
579     ssl_onceonlyinit ();
580     mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);
581 
582 #if	PUBCOOKIE
583     /* if REMOTE_USER set, use it as username */
584     if(buf = getenv("REMOTE_USER"))
585       env_init(buf, "/tmp");
586 #endif
587 
588     if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){
589 	fprintf(stderr, "Can't disable unix driver");
590 	exit(1);
591     }
592 
593     /*
594      * Set network timeouts so we don't hang forever
595      * The open timeout can be pretty short since we're
596      * just opening tcp connection.  The read timeout needs
597      * to be longer because the response to some actions can
598      * take awhile. Hopefully this is well within httpd's
599      * cgi timeout threshold.
600      */
601     mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
602     mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
603 
604     /*----------------------------------------------------------------------
605            Initialize pith library
606       ----------------------------------------------------------------------*/
607     pith_opt_remote_pinerc_failure = remote_pinerc_failure;
608     pith_opt_user_agent_prefix = peWebAlpinePrefix;
609     pith_opt_newmail_announce = peNewMailAnnounce;
610 
611     setup_for_index_index_screen();
612 
613 
614     /*----------------------------------------------------------------------
615            Parse arguments
616       ----------------------------------------------------------------------*/
617     debug = 0;
618     for(argerr = 0; !argerr && ((n = getopt(argc,argv,"d")) != -1); ) {
619 	switch(n) {
620 	  case 'd' : debug++; break;
621 	  case '?' : argerr = 1; break;
622 	}
623     }
624 
625     if(argerr || optind != argc){
626 	char *p = strrchr(argv[0],'/');
627 	fprintf(stderr, "Usage: %s [-d]\n", p ? p + 1 : argv[0]);
628 	exit(1);
629     }
630 
631     /*----------------------------------------------------------------------
632            Hop into the Tcl processing loop
633       ----------------------------------------------------------------------*/
634 
635     buf = (char *) fs_get(bl * sizeof(char));
636 
637     if(fgets(sname, 255, stdin) && *sname){
638 	if(sname[l = strlen(sname) - 1] == '\n')
639 	  sname[l] = '\0';
640 
641 	if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){
642 
643 	    name.sun_family = AF_UNIX;
644 	    strcpy(name.sun_path, peSocketName = sname);
645 	    l = sizeof(name);
646 
647 	    if(bind(s, (struct sockaddr *) &name, l) == 0){
648 		if(listen(s, 5) == 0){
649 		    /*
650 		     * after the groundwork's done, go into the background.
651 		     * the fork saves the caller from invoking us in the background
652 		     * which introduces a timing race between the first client
653 		     * request arrival and our being prepared to accept it.
654 		     */
655 		    if(debug < 10){
656 			switch(fork()){
657 			  case -1 :		/* error */
658 			    perror("fork");
659 			    exit(1);
660 
661 			  case 0 :		/* child */
662 			    close(0);		/* disassociate */
663 			    close(1);
664 			    close(2);
665 			    setpgrp(0, 0);
666 			    break;
667 
668 			  default :		/* parent */
669 			    exit(0);
670 			}
671 		    }
672 
673 		    debug_init();
674 		    dprint((SYSDBG_INFO, "started"));
675 
676 		    interp = Tcl_CreateInterp();
677 
678 		    PEInit(interp, sname);
679 
680 		    while(1){
681 			struct timeval tv;
682 			fd_set	       rfd;
683 
684 			FD_ZERO(&rfd);
685 			FD_SET(s, &rfd);
686 			tv.tv_sec = (gPEAbandonTimeout) ? gPEAbandonTimeout : gPeInputTimeout;
687 			tv.tv_usec = 0;
688 			if((n = select(s+1, &rfd, 0, 0, &tv)) > 0){
689 			    socklen_t ll = l;
690 
691 			    gPEAbandonTimeout = 0;
692 
693 			    if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){
694 				dprint((SYSDBG_ERR, "accept failure: %s",
695 					error_description(errno)));
696 				break;
697 			    }
698 
699 			    dprint((5, "accept success: %d", cs));
700 
701 			    /*
702 			     * tcl commands are prefixed with a number representing
703 			     * the length of the command string and a newline character.
704 			     * the characters representing the length and the newline
705 			     * are not included in the command line length calculation.
706 			     */
707 			    o = co = 0;
708 			    while((n = read(cs, buf + o, bl - o - 1)) > 0){
709 				o += n;
710 				if(!co){
711 				    int i, x = 0;
712 
713 				    for(i = 0; i < o; i++)
714 				      if(buf[i] == '\n'){
715 					  co = ++i;
716 					  l = x + co;
717 					  if(bl < l + 1){
718 					      bl = l + 1;
719 					      fs_resize((void **) &buf, bl * sizeof(char));
720 					  }
721 
722 					  break;
723 				      }
724 				      else
725 					x = (x * 10) + (buf[i] - '0');
726 				}
727 
728 				if(o && o == l)
729 				  break;
730 			    }
731 
732 			    if(n == 0){
733 				dprint((SYSDBG_ERR, "read EOF"));
734 			    }
735 			    else if(n < 0){
736 				dprint((SYSDBG_ERR, "read failure: %s", error_description(errno)));
737 			    }
738 			    else{
739 				buf[o] = '\0';
740 
741 				/* Log every Eval if somebody *really* wants to see it. */
742 				if(debug > 6){
743 				    char dbuf[5120];
744 				    int  dlim = (debug >= 9) ? 256 : 5120 - 32;
745 
746 				    snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]);
747 
748 				    /* But DON'T log any clear-text credentials */
749 				    if(dbuf[9] == 'P'
750 				       && dbuf[10] == 'E'
751 				       && dbuf[11] == 'S'
752 				       && !strncmp(dbuf + 12, "ession creds ", 13)){
753 					char *p;
754 
755 					for(p = &dbuf[25]; *p; p++)
756 					  *p = 'X';
757 				    }
758 
759 				    dprint((1, dbuf));
760 				}
761 
762 				switch(Tcl_Eval(interp, &buf[co])){
763 				  case TCL_OK	  : peReturn(cs, "OK", Tcl_GetStringResult(interp)); break;
764 				  case TCL_ERROR  : peReturn(cs, "ERROR", Tcl_GetStringResult(interp)); break;
765 				  case TCL_BREAK  : peReturn(cs, "BREAK", Tcl_GetStringResult(interp)); break;
766 				  case TCL_RETURN : peReturn(cs, "RETURN", Tcl_GetStringResult(interp)); break;
767 				  default	  : peReturn(cs, "BOGUS", "eval returned unexpected value"); break;
768 				}
769 			    }
770 
771 			    close(cs);
772 			}
773 			else if(errno != EINTR){
774 			    if(n < 0){
775 				dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno)));
776 			    }
777 			    else{
778 				dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec));
779 			    }
780 
781 			    Tcl_Exit(0);
782 
783 			    /* Tcl_Exit should never return. Getting here is an error. */
784 			    dprint((SYSDBG_ERR, "Tcl_Exit failure"));
785 			}
786 		    }
787 		}
788 		else
789 		  perror("listen");
790 	    }
791 	    else
792 	      perror("bind");
793 
794 	    close(s);
795 	    unlink(sname);
796 	}
797 	else
798 	  perror("socket");
799     }
800     else
801       fprintf(stderr, "Can't read socket name\n");
802 
803     exit(ev);
804 }
805 
806 
807 /*
808  * peReturn - common routine to return TCL result
809  */
810 void
peReturn(int sock,char * status,const char * result)811 peReturn(int sock, char *status, const char *result)
812 {
813     if(peWrite(sock, status))
814       if(peWrite(sock, "\n"))
815 	peWrite(sock, (char *) result);
816 }
817 
818 /*
819  * peWrite - write all the given string on the given socket
820  */
821 int
peWrite(int sock,char * s)822 peWrite(int sock, char *s)
823 {
824     int i, n;
825 
826     for(i = 0, n = strlen(s); n; n = n - i)
827       if((i = write(sock, s + i, n)) < 0){
828 	  dprint((SYSDBG_ERR, "write: %s", error_description(errno)));
829 	  return(0);
830       }
831 
832     return(1);
833 }
834 
835 /*
836  * PEInit - Initialize exported TCL functions
837  */
838 int
PEInit(Tcl_Interp * interp,char * sname)839 PEInit(Tcl_Interp *interp, char *sname)
840 {
841     dprint((2, "PEInit: %s", sname));
842 
843     if(Tcl_Init(interp) == TCL_ERROR) {
844 	return(TCL_ERROR);
845     }
846 
847     Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd,
848 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
849 
850     Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd,
851 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
852 
853     Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd,
854 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
855 
856     Tcl_CreateObjCommand(interp, "PESession", PESessionCmd,
857 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
858 
859     Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd,
860 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
861 
862     Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd,
863 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
864 
865     Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd,
866 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
867 
868     Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd,
869 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
870 
871     Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd,
872 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
873 
874     Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd,
875 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
876 
877     Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd,
878 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
879 
880     Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd,
881 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
882 
883     Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd,
884 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
885 
886     Tcl_CreateObjCommand(interp, "PERss", PERssCmd,
887 			(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
888 
889     Tcl_CreateExitHandler(PEExitCleanup, sname);
890 
891 #ifdef ENABLE_LDAP
892     wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
893     wpldap_global->query_no = 0;
894     wpldap_global->ldap_search_list = NULL;
895 #endif /* ENABLE_LDAP */
896 
897     return(TCL_OK);
898 }
899 
900 
901 void
PEExitCleanup(ClientData clientData)902 PEExitCleanup(ClientData clientData)
903 {
904     dprint((4, "PEExitCleanup"));
905 
906     if(wps_global){
907 	/* destroy any open stream */
908 	peDestroyStream(wps_global);
909 
910 	/* destroy user context */
911 	peDestroyUserContext(&wps_global);
912     }
913 
914 #ifdef ENABLE_LDAP
915     if(wpldap_global){
916         if(wpldap_global->ldap_search_list)
917 	  free_wpldapres(wpldap_global->ldap_search_list);
918 	fs_give((void **)&wpldap_global);
919     }
920 #endif /* ENABLE_LDAP */
921 
922     if((char *) clientData)
923       unlink((char *) clientData);
924 
925     peFreeAttach(&peCompAttach);
926 
927     dprint((SYSDBG_INFO, "finished"));
928 }
929 
930 
931 /*
932  * PEInfoCmd - export various bits of alpine state
933  */
934 int
PEInfoCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])935 PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
936 {
937     char *err = "Unknown PEInfo request";
938 
939     dprint((2, "PEInfoCmd"));
940 
941     if(objc == 1){
942 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
943     }
944     else{
945 	char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
946 
947 	if(s1){
948 	    if(!strcmp(s1, "colorset")){
949 		char *varname, *fghex, *bghex;
950 		char  tvname[256], asciicolor[256];
951 		struct  variable *vtmp;
952 		Tcl_Obj **cObj;
953 		int       cObjc;
954 		SPEC_COLOR_S *hcolors, *thc;
955 
956 		if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
957 		    Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
958 		    return(TCL_ERROR);
959 		}
960 
961 		if(!strcmp(varname, "viewer-hdr-colors")){
962 		    char *newhdr = NULL, *newpat = NULL, *utype;
963 		    int   hindex, i;
964 
965 		    if(objc < 5){
966 			Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
967 			return(TCL_ERROR);
968 		    }
969 
970 		    hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
971 		    if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
972 			Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
973 			return(TCL_ERROR);
974 		    }
975 
976 		    if(!strcmp(utype, "delete")){
977 			if(!hcolors){
978 			    Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
979 			    return(TCL_ERROR);
980 			}
981 
982 			if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
983 			    Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
984 			    return(TCL_ERROR);
985 			}
986 
987 			if(hindex == 0){
988 			    thc = hcolors;
989 			    hcolors = hcolors->next;
990 			    thc->next = NULL;
991 			    free_spec_colors(&thc);
992 			}
993 			else{
994 			    /* zero based */
995 			    for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
996 			      ;
997 
998 			    if(thc && thc->next){
999 				SPEC_COLOR_S *thc2 = thc->next;
1000 
1001 				thc->next = thc2->next;
1002 				thc2->next = NULL;
1003 				free_spec_colors(&thc2);
1004 			    }
1005 			    else{
1006 				Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
1007 				return(TCL_ERROR);
1008 			    }
1009 			}
1010 		    }
1011 		    else if(!strcmp(utype, "add")){
1012 			if(objc != 6){
1013 			    Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
1014 			    return(TCL_ERROR);
1015 			}
1016 
1017 			if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
1018 			  return (TCL_ERROR);
1019 
1020 			if(cObjc != 2){
1021 			    Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
1022 			    return(TCL_ERROR);
1023 			}
1024 
1025 			newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
1026 			newpat = Tcl_GetStringFromObj(cObj[1], NULL);
1027 			if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
1028 			  return (TCL_ERROR);
1029 
1030 			if(cObjc != 2){
1031 			    Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
1032 			    return(TCL_ERROR);
1033 			}
1034 
1035 			fghex = Tcl_GetStringFromObj(cObj[0], NULL);
1036 			bghex = Tcl_GetStringFromObj(cObj[1], NULL);
1037 			if(newhdr && newpat && fghex && bghex){
1038 			    SPEC_COLOR_S **hcp;
1039 
1040 			    for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
1041 			      ;
1042 
1043 			    *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
1044 			    (*hcp)->inherit = 0;
1045 			    (*hcp)->spec = cpystr(newhdr);
1046 			    (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
1047 			    (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
1048 
1049 			    if(newpat && *newpat)
1050 			      (*hcp)->val = string_to_pattern(newpat);
1051 			    else
1052 			      (*hcp)->val = NULL;
1053 
1054 			    (*hcp)->next = NULL;
1055 			}
1056 			else{
1057 			    Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
1058 			    return(TCL_ERROR);
1059 			}
1060 		    }
1061 		    else if(!strcmp(utype, "update")){
1062 			if(objc != 6){
1063 			    Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
1064 			    return(TCL_ERROR);
1065 			}
1066 
1067 			if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
1068 			     && cObjc == 3
1069 			     && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
1070 			     && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
1071 			     && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
1072 			    Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
1073 			    return (TCL_ERROR);
1074 			}
1075 
1076 			if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
1077 			     && cObjc == 2
1078 			     && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
1079 			     && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
1080 			    Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
1081 			    return (TCL_ERROR);
1082 			}
1083 
1084 			for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
1085 			  ;
1086 
1087 			if(!thc){
1088 			    Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
1089 			    return (TCL_ERROR);
1090 			}
1091 
1092 			if(thc->spec)
1093 			  fs_give((void **)&thc->spec);
1094 
1095 			thc->spec = cpystr(newhdr);
1096 			if(ascii_colorstr(asciicolor, fghex) == 0) {
1097 			    if(thc->fg)
1098 			      fs_give((void **)&thc->fg);
1099 
1100 			    thc->fg = cpystr(asciicolor);
1101 			}
1102 			else{
1103 			    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
1104 			    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1105 			    return(TCL_ERROR);
1106 			}
1107 
1108 			if(ascii_colorstr(asciicolor, bghex) == 0) {
1109 			    if(thc->bg)
1110 			      fs_give((void **)&thc->bg);
1111 
1112 			    thc->bg = cpystr(asciicolor);
1113 			}
1114 			else{
1115 			    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1116 			    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1117 			    return(TCL_ERROR);
1118 			}
1119 
1120 			if(thc->val)
1121 			  fs_give((void **)&thc->val);
1122 
1123 			if(newpat && *newpat){
1124 			    thc->val = string_to_pattern(newpat);
1125 			}
1126 		    }
1127 		    else{
1128 			Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
1129 			return(TCL_ERROR);
1130 		    }
1131 
1132 		    vtmp = &wps_global->vars[V_VIEW_HDR_COLORS];
1133 		    for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
1134 		      fs_give((void **)&vtmp->main_user_val.l[i]);
1135 
1136 		    if(vtmp->main_user_val.l)
1137 		      fs_give((void **)&vtmp->main_user_val.l);
1138 
1139 		    vtmp->main_user_val.l = varlist_from_spec_colors(hcolors);
1140 		    set_current_val(vtmp, FALSE, FALSE);
1141 		    free_spec_colors(&hcolors);
1142 		    return(TCL_OK);
1143 		}
1144 		else {
1145 		    if(objc != 4){
1146 			Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
1147 			return(TCL_ERROR);
1148 		    }
1149 
1150 		    if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
1151 			 && cObjc == 2
1152 			 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
1153 			 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
1154 			Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
1155 			return (TCL_ERROR);
1156 		    }
1157 
1158 		    snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
1159 		    for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1160 			vtmp->name && strucmp(vtmp->name, tvname);
1161 			vtmp++)
1162 		      ;
1163 
1164 		    if(!vtmp->name || vtmp->is_list){
1165 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1166 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1167 			return(TCL_ERROR);
1168 		    }
1169 
1170 		    if(ascii_colorstr(asciicolor, fghex) == 0) {
1171 			if(vtmp->main_user_val.p)
1172 			  fs_give((void **)&vtmp->main_user_val.p);
1173 
1174 			vtmp->main_user_val.p = cpystr(asciicolor);
1175 			set_current_val(vtmp, FALSE, FALSE);
1176 			if(!strucmp(varname, "normal"))
1177 			  pico_set_fg_color(asciicolor);
1178 		    }
1179 		    else{
1180 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
1181 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1182 			return(TCL_ERROR);
1183 		    }
1184 
1185 		    snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
1186 		    vtmp++;
1187 		    if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
1188 		      for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1189 			  vtmp->name && strucmp(vtmp->name, tvname);
1190 			  vtmp++)
1191 			;
1192 
1193 		    if(!vtmp->name || vtmp->is_list){
1194 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1195 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1196 			return(TCL_ERROR);
1197 		    }
1198 
1199 		    if(ascii_colorstr(asciicolor, bghex) == 0) {
1200 			if(vtmp->main_user_val.p)
1201 			  fs_give((void **)&vtmp->main_user_val.p);
1202 
1203 			vtmp->main_user_val.p = cpystr(asciicolor);
1204 			set_current_val(vtmp, FALSE, FALSE);
1205 			if(!strucmp(varname, "normal"))
1206 			  pico_set_bg_color(asciicolor);
1207 		    }
1208 		    else{
1209 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1210 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1211 			return(TCL_ERROR);
1212 		    }
1213 
1214 		    Tcl_SetResult(interp, "1", TCL_STATIC);
1215 		    return(TCL_OK);
1216 		}
1217 	    }
1218 	    else if(!strcmp(s1, "lappend")){
1219 		if(objc >= 4){
1220 		    Tcl_Obj *dObj;
1221 		    int	     i;
1222 
1223 		    if((dObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
1224 			for(i = 3; i < objc; i++)
1225 			  if(Tcl_ListObjAppendElement(interp, dObj, objv[i]) != TCL_OK)
1226 			    return(TCL_ERROR);
1227 
1228 			if(i == objc){
1229 			    return(TCL_OK);
1230 			}
1231 		    }
1232 		    else
1233 		      err = "PEInfo lappend: Unknown list name";
1234 		}
1235 		else
1236 		  err = "PEInfo lappend: Too few args";
1237 	    }
1238 	    else if(objc == 2){
1239 		if(!strcmp(s1, "version")){
1240 		    char buf[256];
1241 
1242 		    /*
1243 		     * CMD: version
1244 		     *
1245 		     * Returns: string representing Pine version
1246 		     * engine built on
1247 		     */
1248 		    Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC);
1249 		    return(TCL_OK);
1250 		}
1251 		else if(!strcmp(s1, "revision")){
1252 		    char buf[16];
1253 
1254 		    /*
1255 		     * CMD: revision
1256 		     *
1257 		     * Returns: string representing Pine SVN revision
1258 		     * engine built on
1259 		     */
1260 
1261 		    Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE);
1262 		    return(TCL_OK);
1263 		}
1264 		else if(!strcmp(s1, "key")){
1265 		    static char key[64];
1266 
1267 		    if(!key[0])
1268 		      peRandomString(key,32,PRS_UPPER_CASE);
1269 
1270 		    Tcl_SetResult(interp, key, TCL_STATIC);
1271 		    return(TCL_OK);
1272 		}
1273 		else if(!strcmp(s1, "indexheight")){
1274 		    Tcl_SetResult(interp, wps_global->VAR_WP_INDEXHEIGHT ?
1275 				  wps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE);
1276 		    return(TCL_OK);
1277 		}
1278 		else if(!strcmp(s1, "indexlines")){
1279 		    Tcl_SetResult(interp, wps_global->VAR_WP_INDEXLINES ?
1280 				  wps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE);
1281 		    return(TCL_OK);
1282 		}
1283 		else if(!strcmp(s1, "aggtabstate")){
1284 		    Tcl_SetResult(interp, wps_global->VAR_WP_AGGSTATE ?
1285 				  wps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE);
1286 		    return(TCL_OK);
1287 		}
1288 		else if(!strcmp(s1, "alpinestate")){
1289                     char *wps, *p, *q;
1290 
1291                     if((wps = wps_global->VAR_WP_STATE) != NULL){
1292                         wps = p = q = cpystr(wps);
1293                         do
1294                           if(*q == '\\' && *(q+1) == '$')
1295                             q++;
1296                         while((*p++ = *q++) != '\0');
1297                     }
1298 
1299                     Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE);
1300 
1301                     if(wps)
1302                       fs_give((void **) &wps);
1303 
1304 		    return(TCL_OK);
1305 		}
1306 		else if(!strcmp(s1, "foreground")){
1307 		    char *color;
1308 
1309 		    if(!((color = pico_get_last_fg_color())
1310 			 && (color = color_to_asciirgb(color))
1311 			 && (color = peColorStr(color,wtmp_20k_buf))))
1312 		      color = "000000";
1313 
1314 		    Tcl_SetResult(interp, color, TCL_VOLATILE);
1315 		    return(TCL_OK);
1316 		}
1317 		else if(!strcmp(s1, "background")){
1318 		    char *color;
1319 
1320 		    if(!((color = pico_get_last_bg_color())
1321 			 && (color = color_to_asciirgb(color))
1322 			 && (color = peColorStr(color,wtmp_20k_buf))))
1323 		      color = "FFFFFF";
1324 
1325 		    Tcl_SetResult(interp, color, TCL_VOLATILE);
1326 		    return(TCL_OK);
1327 		}
1328 		else if(!strcmp(s1, "flaglist")){
1329 		    int	       i;
1330 		    char      *p;
1331 		    Tcl_Obj   *itemObj;
1332 
1333 		    /*
1334 		     * BUG: This list should get merged with the static list in "cmd_flag"
1335 		     * and exported via some function similar to "feature_list()"
1336 		     */
1337 		    static char *flag_list[] = {
1338 			"Important", "New", "Answered", "Deleted", NULL
1339 		    };
1340 
1341 		    /*
1342 		     * CMD: flaglist
1343 		     *
1344 		     * Returns: list of FLAGS available for setting
1345 		     */
1346 		    for(i = 0; (p = flag_list[i]); i++)
1347 		      if((itemObj = Tcl_NewStringObj(p, -1)) != NULL){
1348 			  if(Tcl_ListObjAppendElement(interp,
1349 						      Tcl_GetObjResult(interp),
1350 						      itemObj) != TCL_OK)
1351 			    ;
1352 		      }
1353 
1354 		    return(TCL_OK);
1355 		}
1356 		else if(!strcmp(s1, "featurelist")){
1357 		    int	       i;
1358 		    char      *curfeature, *s;
1359 		    FEATURE_S *feature;
1360 		    Tcl_Obj   *itemObj, *secObj = NULL, *resObj = NULL;
1361 
1362 		    /*
1363 		     * CMD: featurelist
1364 		     *
1365 		     * Returns: list of FEATURES available for setting
1366 		     */
1367 		    for(i = 0, curfeature = NULL; (feature = feature_list(i)); i++)
1368 		      if((s = feature_list_section(feature)) != NULL){
1369 			  if(!curfeature || strucmp(s, curfeature)){
1370 			      if(resObj) {
1371 				  Tcl_ListObjAppendElement(interp,
1372 							   secObj,
1373 							   resObj);
1374 				  Tcl_ListObjAppendElement(interp,
1375 							   Tcl_GetObjResult(interp),
1376 							   secObj);
1377 			      }
1378 
1379 			      secObj = Tcl_NewListObj(0, NULL);
1380 			      resObj = Tcl_NewListObj(0, NULL);
1381 			      if(Tcl_ListObjAppendElement(interp,
1382 							  secObj,
1383 							  Tcl_NewStringObj(s,-1)) != TCL_OK)
1384 				;
1385 
1386 			      curfeature = s;
1387 			  }
1388 
1389 			  if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1390 			      if(Tcl_ListObjAppendElement(interp,
1391 							  resObj,
1392 							  itemObj) != TCL_OK)
1393 				;
1394 			  }
1395 		      }
1396 
1397 		    if(resObj){
1398 			Tcl_ListObjAppendElement(interp, secObj, resObj);
1399 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj);
1400 		    }
1401 
1402 		    return(TCL_OK);
1403 		}
1404 		else if(!strcmp(s1, "featuresettings")){
1405 		    int	       i;
1406 		    FEATURE_S *feature;
1407 		    Tcl_Obj   *itemObj;
1408 
1409 		    /*
1410 		     * CMD: featuresettings
1411 		     *
1412 		     * Returns: list of FEATURES currently SET
1413 		     */
1414 		    for(i = 0; (feature = feature_list(i)); i++)
1415 		      if(feature_list_section(feature)){
1416 			  if(F_ON(feature->id, wps_global)){
1417 			      if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1418 				  if(Tcl_ListObjAppendElement(interp,
1419 							      Tcl_GetObjResult(interp),
1420 							      itemObj) != TCL_OK)
1421 				    ;
1422 			      }
1423 			  }
1424 		      }
1425 
1426 		    return(TCL_OK);
1427 		}
1428 		else if(!strcmp(s1, "signature")){
1429 		    char *sig;
1430 
1431 		    if((wps_global->VAR_LITERAL_SIG
1432 		       || (wps_global->VAR_SIGNATURE_FILE
1433 			   && IS_REMOTE(wps_global->VAR_SIGNATURE_FILE)))
1434 		       && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){
1435 			char *p, *q;
1436 
1437 			for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1438 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1439 						   Tcl_NewStringObj(p, q - p));
1440 
1441 			if(*p)
1442 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1443 						   Tcl_NewStringObj(p, -1));
1444 
1445 			fs_give((void **) &sig);
1446 		    }
1447 		    else
1448 		      Tcl_SetResult(interp, "", TCL_STATIC);
1449 
1450 		    return(TCL_OK);
1451 		}
1452 		else if(!strcmp(s1, "rawsig")){
1453 		    char *err = NULL, *sig = NULL, *p, *q;
1454 
1455 		    if(wps_global->VAR_LITERAL_SIG){
1456 			char     *err = NULL;
1457 			char    **apval;
1458 
1459 			if(wps_global->restricted){
1460 			    err = "Alpine demo can't change config file";
1461 			}
1462 			else{
1463 			    /* BUG: no "exceptions file" support */
1464 			    if((apval = APVAL(&wps_global->vars[V_LITERAL_SIG], Main)) != NULL){
1465 				sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
1466 				sig[0] = '\0';
1467 				cstring_to_string(*apval, sig);
1468 			    }
1469 			    else
1470 			      err = "Problem accessing configuration";
1471 			}
1472 		    }
1473 		    else if(!IS_REMOTE(wps_global->VAR_SIGNATURE_FILE))
1474 		      snprintf(err = wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
1475 			      wps_global->VAR_SIGNATURE_FILE ? wps_global->VAR_SIGNATURE_FILE : "<null>");
1476 		    else if(!(sig = simple_read_remote_file(wps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))
1477 		      err = "Can't read remote pinerc";
1478 
1479 		    if(err){
1480 			Tcl_SetResult(interp, err, TCL_VOLATILE);
1481 			return(TCL_ERROR);
1482 		    }
1483 
1484 		    for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1485 		      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1486 					       Tcl_NewStringObj(p, q - p));
1487 
1488 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1489 					     Tcl_NewStringObj(p, -1));
1490 		    fs_give((void **) &sig);
1491 		    return(TCL_OK);
1492 		}
1493 		else if(!strcmp(s1, "statmsg")){
1494 		    char *s = sml_getmsg();
1495 		    /* BUG: can this be removed? */
1496 
1497 		    Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE);
1498 		    return(TCL_OK);
1499 		}
1500 		else if(!strcmp(s1, "statmsgs")){
1501 		    char **s = sml_getmsgs();
1502 		    char **tmps, *lmsg = NULL;
1503 
1504 		    for(tmps = s; tmps && *tmps; lmsg = *tmps++)
1505 		      if(!lmsg || strcmp(lmsg, *tmps))
1506 			Tcl_ListObjAppendElement(interp,
1507 						 Tcl_GetObjResult(interp),
1508 						 Tcl_NewStringObj(*tmps, -1));
1509 
1510 		    fs_give((void **)&s);
1511 		    return(TCL_OK);
1512 		}
1513 		else if(!strcmp(s1, "saveconf")){
1514 		    write_pinerc(wps_global, Main, WRP_NOUSER);
1515 		    return(TCL_OK);
1516 		}
1517 		else if(!strucmp(s1, "sort")){
1518 		    return(peAppendDefaultSort(interp));
1519 		}
1520 		else if(!strcmp(s1, "ldapenabled")){
1521 		    /*
1522 		     * CMD: ldapenabled
1523 		     *
1524 		     * Returns: 1 if enabled 0 if not
1525 		     */
1526 #ifdef ENABLE_LDAP
1527 		    Tcl_SetResult(interp, "1", TCL_VOLATILE);
1528 #else
1529 		    Tcl_SetResult(interp, "0", TCL_VOLATILE);
1530 #endif
1531 
1532 		    return(TCL_OK);
1533 		}
1534 		else if(!strcmp(s1, "prunecheck")){
1535 		    time_t  now;
1536 		    struct tm *tm_now;
1537 		    char    tmp[50];
1538 
1539 		    if(!check_prune_time(&now, &tm_now)){
1540 		        Tcl_SetResult(interp, "0", TCL_VOLATILE);
1541 			return(TCL_OK);
1542 		    } else {
1543 		      /*
1544 		       * We're going to reset the last-time-pruned variable
1545 		       * so that it asks a maximum of 1 time per month.
1546 		       * PROs: Annoying-factor is at its lowest
1547 		       *       Can go ahead and move folders right away if
1548 		       *         pruning-rule is automatically set to do so
1549 		       * CONs: Annoying-factor is at its lowest, if it's set
1550 		       *         later then we can ensure that the questions
1551 		       *         actually get answered or it will keep asking
1552 		       */
1553 		      wps_global->last_expire_year = tm_now->tm_year;
1554 		      wps_global->last_expire_month = tm_now->tm_mon;
1555 		      snprintf(tmp, sizeof(tmp), "%d.%d", wps_global->last_expire_year,
1556 			      wps_global->last_expire_month + 1);
1557 		      set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main);
1558 
1559 		      Tcl_SetResult(interp, "1", TCL_VOLATILE);
1560 		    }
1561 		    return(TCL_OK);
1562 		}
1563 		else if(!strcmp(s1, "prunetime")){
1564 		    time_t	 now;
1565 		    struct tm	*tm_now;
1566 		    CONTEXT_S   *prune_cntxt;
1567 		    Tcl_Obj     *retObj = NULL;
1568 		    int          cur_month, ok = 1;
1569 		    char       **p;
1570 		    static int   moved_fldrs = 0;
1571 
1572 		    now = time((time_t *)0);
1573 		    tm_now = localtime(&now);
1574 		    cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
1575 
1576 		    if(!(prune_cntxt = default_save_context(wps_global->context_list)))
1577 		      prune_cntxt = wps_global->context_list;
1578 
1579 		    if(prune_cntxt){
1580 		        if(wps_global->VAR_DEFAULT_FCC && *wps_global->VAR_DEFAULT_FCC
1581 			   && context_isambig(wps_global->VAR_DEFAULT_FCC))
1582 			    if((retObj = wp_prune_folders(prune_cntxt,
1583 							  wps_global->VAR_DEFAULT_FCC,
1584 							  cur_month, "sent",
1585 							  wps_global->pruning_rule, &ok,
1586 							  moved_fldrs, interp)) != NULL)
1587 			        Tcl_ListObjAppendElement(interp,
1588 							 Tcl_GetObjResult(interp),
1589 							 retObj);
1590 
1591 			if(ok && wps_global->VAR_READ_MESSAGE_FOLDER
1592 			   && *wps_global->VAR_READ_MESSAGE_FOLDER
1593 			   && context_isambig(wps_global->VAR_READ_MESSAGE_FOLDER))
1594 			    if((retObj = wp_prune_folders(prune_cntxt,
1595 							  wps_global->VAR_READ_MESSAGE_FOLDER,
1596 							  cur_month, "read",
1597 							  wps_global->pruning_rule, &ok,
1598 							  moved_fldrs, interp)) != NULL)
1599 			        Tcl_ListObjAppendElement(interp,
1600 							 Tcl_GetObjResult(interp),
1601 							 retObj);
1602 			if(ok && (p = wps_global->VAR_PRUNED_FOLDERS)){
1603 			    for(; ok && *p; p++)
1604 			      if(**p && context_isambig(*p))
1605 				    if((retObj = wp_prune_folders(prune_cntxt,
1606 							   *p, cur_month, "",
1607 							    wps_global->pruning_rule, &ok,
1608 							    moved_fldrs, interp)) != NULL)
1609 				        Tcl_ListObjAppendElement(interp,
1610 								 Tcl_GetObjResult(interp),
1611 								 retObj);
1612 			}
1613 		    }
1614 		    moved_fldrs = 1;
1615 		    return(TCL_OK);
1616 		}
1617 		else if(!strcmp(s1, "authrequestor")){
1618 		    Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC);
1619 		    return(TCL_OK);
1620 		}
1621 		else if(!strcmp(s1, "noop")){
1622 		    /* tickle the imap server too */
1623 		    if(wps_global->mail_stream)
1624 		      pine_mail_ping(wps_global->mail_stream);
1625 
1626 		    Tcl_SetResult(interp, "NOOP", TCL_STATIC);
1627 		    return(TCL_OK);
1628 		}
1629 		else if(!strcmp(s1, "inputtimeout")){
1630 		    Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE);
1631 		    return(TCL_OK);
1632 		}
1633 	    }
1634 	    else if(objc == 3){
1635 		if(!strcmp(s1, "feature")){
1636 		    char      *featurename;
1637 		    int	       i, isset = 0;
1638 		    FEATURE_S *feature;
1639 
1640 		    /*
1641 		     * CMD: feature
1642 		     *
1643 		     * ARGS: featurename -
1644 		     *
1645 		     * Returns: 1 if named feature set, 0 otherwise
1646 		     *
1647 		     */
1648 		    if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
1649 		      for(i = 0; (feature = feature_list(i)); i++)
1650 			if(!strucmp(featurename, feature->name)){
1651 			    isset = F_ON(feature->id, wps_global);
1652 			    break;
1653 			}
1654 
1655 		    Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE);
1656 		    return(TCL_OK);
1657 		}
1658 		else if(!strcmp(s1, "colorget")){
1659 		    char             *varname;
1660 		    char              tvname[256], hexcolor[256];
1661 		    struct  variable *vtmp;
1662 		    if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
1663 		      return(TCL_ERROR);
1664 		    }
1665 		    if(strcmp("viewer-hdr-colors", varname) == 0){
1666 			SPEC_COLOR_S *hcolors, *thc;
1667 			Tcl_Obj     *resObj;
1668 			char         hexcolor[256], *tstr = NULL;
1669 
1670 			hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
1671 			for(thc = hcolors; thc; thc = thc->next){
1672 			    resObj = Tcl_NewListObj(0,NULL);
1673 			    Tcl_ListObjAppendElement(interp, resObj,
1674 						     Tcl_NewStringObj(thc->spec, -1));
1675 			    hex_colorstr(hexcolor, thc->fg);
1676 			    Tcl_ListObjAppendElement(interp, resObj,
1677 						     Tcl_NewStringObj(hexcolor, -1));
1678 			    hex_colorstr(hexcolor, thc->bg);
1679 			    Tcl_ListObjAppendElement(interp, resObj,
1680 						     Tcl_NewStringObj(hexcolor, -1));
1681 			    Tcl_ListObjAppendElement(interp, resObj,
1682 					Tcl_NewStringObj(thc->val
1683 					? tstr = pattern_to_string(thc->val)
1684 							 : "", -1));
1685 			    if(tstr) fs_give((void **)&tstr);
1686 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1687 						     resObj);
1688 			}
1689 			fs_give((void **)&hcolors);
1690 			return(TCL_OK);
1691 		    }
1692 		    else {
1693 			snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
1694 			for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1695 			    vtmp->name && strucmp(vtmp->name, tvname);
1696 			    vtmp++);
1697 			if(!vtmp->name) return(TCL_ERROR);
1698 			if(vtmp->is_list) return(TCL_ERROR);
1699 			if(!vtmp->current_val.p)
1700 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1701 						   Tcl_NewStringObj("", -1));
1702 			else{
1703 			    hex_colorstr(hexcolor, vtmp->current_val.p);
1704 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1705 						     Tcl_NewStringObj(hexcolor, -1));
1706 			}
1707 			snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
1708 			vtmp++;
1709 			if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
1710 			  for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1711 			      vtmp->name && strucmp(vtmp->name, tvname);
1712 			      vtmp++)
1713 			    ;
1714 
1715 			if(!vtmp->name) return(TCL_ERROR);
1716 			if(vtmp->is_list) return(TCL_ERROR);
1717 			if(!vtmp->current_val.p)
1718 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1719 						   Tcl_NewStringObj("", -1));
1720 			else{
1721 			    hex_colorstr(hexcolor, vtmp->current_val.p);
1722 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1723 						     Tcl_NewStringObj(hexcolor, -1));
1724 			}
1725 		    }
1726 		    return(TCL_OK);
1727 		}
1728 		else if(!strcmp(s1, "varget")){
1729 		    struct     variable *vtmp;
1730 		    Tcl_Obj   *itemObj, *resObj, *secObj;
1731 		    char      *vallist, *varname, tmperrmsg[256];
1732 		    int        i;
1733 		    NAMEVAL_S *tmpnv;
1734 
1735 		    /*
1736 		     * CMD: varget
1737 		     *
1738 		     * Returns: get the values for the requested variable
1739 		     *
1740 		     * The list returned follows this general form:
1741 		     *
1742 		     * char *;  variable name
1743 		     * char **;  list of set values
1744 		     * char *;  display type (listbox, text, textarea, ...)
1745 		     * char **; list of possible values
1746 		     *            (so far this is only useful for listboxes)
1747 		     */
1748 		    if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
1749 		      Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj",
1750 				    TCL_VOLATILE);
1751 		      return(TCL_ERROR);
1752 		    }
1753 
1754 		    for(vtmp = wps_global->vars;
1755 			vtmp->name && strucmp(vtmp->name, varname);
1756 			vtmp++)
1757 		      ;
1758 
1759 		    if(!vtmp->name){
1760 		      snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
1761 			      strlen(varname) < 200 ? varname : "");
1762 		      Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
1763 		      return(TCL_ERROR);
1764 		    }
1765 		    if((itemObj = Tcl_NewStringObj(vtmp->name, -1)) != NULL){
1766 			Tcl_ListObjAppendElement(interp,
1767 						 Tcl_GetObjResult(interp),
1768 						 itemObj);
1769 			resObj = Tcl_NewListObj(0, NULL);
1770 			if(vtmp->is_list){
1771 			    for(i = 0 ; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
1772 			        vallist = vtmp->current_val.l[i];
1773 				if(*(vallist))
1774 				  itemObj = Tcl_NewStringObj(vallist, -1);
1775 				else
1776 				  itemObj = Tcl_NewStringObj("", -1);
1777 				Tcl_ListObjAppendElement(interp, resObj, itemObj);
1778 			    }
1779 			}
1780 			else{
1781 			  itemObj = Tcl_NewStringObj(vtmp->current_val.p ?
1782 						     vtmp->current_val.p : "", -1);
1783 			  Tcl_ListObjAppendElement(interp, resObj, itemObj);
1784 			}
1785 			Tcl_ListObjAppendElement(interp,
1786 						 Tcl_GetObjResult(interp),
1787 						 resObj);
1788 			secObj = Tcl_NewListObj(0, NULL);
1789 			if(vtmp->is_list)
1790 			  itemObj = Tcl_NewStringObj("textarea", -1);
1791 			else{
1792 			  NAMEVAL_S *(*tmpf)(int);
1793 			  switch(vtmp - wps_global->vars){
1794 			    case V_SAVED_MSG_NAME_RULE:
1795 			      tmpf = save_msg_rules;
1796 			      break;
1797 			    case V_FCC_RULE:
1798 			      tmpf = fcc_rules;
1799 			      break;
1800 			    case V_SORT_KEY:
1801 			      tmpf = sort_key_rules;
1802 			      break;
1803 			    case V_AB_SORT_RULE:
1804 			      tmpf = ab_sort_rules;
1805 			      break;
1806 			    case V_FLD_SORT_RULE:
1807 			      tmpf = fld_sort_rules;
1808 			      break;
1809 			    case V_GOTO_DEFAULT_RULE:
1810 			      tmpf = goto_rules;
1811 			      break;
1812 			    case V_INCOMING_STARTUP:
1813 			      tmpf = incoming_startup_rules;
1814 			      break;
1815 			    case V_PRUNING_RULE:
1816 			      tmpf = pruning_rules;
1817 			      break;
1818 			    case V_WP_INDEXHEIGHT:
1819 			      tmpf = wp_indexheight_rules;
1820 			      break;
1821 			    default:
1822 			      tmpf = NULL;
1823 			      break;
1824 			  }
1825 			  if(tmpf){
1826 			    for(i = 0; (tmpnv = (tmpf)(i)); i++){
1827 			      itemObj = Tcl_NewListObj(0, NULL);
1828 			      Tcl_ListObjAppendElement(interp, itemObj,
1829 						       Tcl_NewStringObj(tmpnv->name, -1));
1830 			      if(tmpnv->shortname)
1831 				Tcl_ListObjAppendElement(interp, itemObj,
1832 					     Tcl_NewStringObj(tmpnv->shortname, -1));
1833 			      Tcl_ListObjAppendElement(interp, secObj, itemObj);
1834 			    }
1835 			    itemObj = Tcl_NewStringObj("listbox", -1);
1836 			  }
1837 			  else
1838 			    itemObj = Tcl_NewStringObj("text", -1);
1839 			}
1840 			Tcl_ListObjAppendElement(interp,
1841 						 Tcl_GetObjResult(interp),
1842 						 itemObj);
1843 			Tcl_ListObjAppendElement(interp,
1844 						 Tcl_GetObjResult(interp),
1845 						 secObj);
1846 		    }
1847 		    return(TCL_OK);
1848 		}
1849 		else if(!strcmp(s1, "rawsig")){
1850 
1851 		    if(wps_global->VAR_LITERAL_SIG){
1852 			char	 *cstring_version, *sig, *line;
1853 			int	  i, nSig;
1854 			Tcl_Obj **objSig;
1855 
1856 			wtmp_20k_buf[0] = '\0';
1857 			Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
1858 			for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
1859 			  if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
1860 			    snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
1861 
1862 			sig = cpystr(wtmp_20k_buf);
1863 
1864 			if((cstring_version = string_to_cstring(sig)) != NULL){
1865 			    set_variable(V_LITERAL_SIG, cstring_version, 0, 0, Main);
1866 			    fs_give((void **)&cstring_version);
1867 			}
1868 
1869 			fs_give((void **) &sig);
1870 			return(TCL_OK);
1871 		    }
1872 		    else
1873 		      return(peWriteSig(interp, wps_global->VAR_SIGNATURE_FILE,
1874 					&((Tcl_Obj **)objv)[2]));
1875 		}
1876 		else if(!strcmp(s1, "statmsg")){
1877 		    char *msg;
1878 
1879 		    /*
1880 		     * CMD: statmsg
1881 		     *
1882 		     * ARGS: msg - text to set
1883 		     *
1884 		     * Returns: nothing, but with global status message
1885 		     *		buf set to given msg
1886 		     *
1887 		     */
1888 		    if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
1889 		      sml_addmsg(0, msg);
1890 
1891 		    return(TCL_OK);
1892 		}
1893 		else if(!strcmp(s1, "mode")){
1894 		    char *mode;
1895 		    int	  rv = 0;
1896 
1897 		    /*
1898 		     * CMD: mode
1899 		     *
1900 		     * ARGS: <mode>
1901 		     *
1902 		     * Returns: return value of given binary mode
1903 		     *
1904 		     */
1905 		    if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1906 			if(!strcmp(mode, "full-header-mode"))
1907 			  rv = wps_global->full_header;
1908 		    }
1909 
1910 		    Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
1911 		    return(TCL_OK);
1912 		}
1913 		else if(!strcmp(s1, "indexlines")){
1914 		    int   n;
1915 		    char *p;
1916 
1917 		    if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
1918 			set_variable(V_WP_INDEXLINES, p = int2string(n), 0, 0, Main);
1919 			Tcl_SetResult(interp, p, TCL_VOLATILE);
1920 		    }
1921 		    return(TCL_OK);
1922 		}
1923 		else if(!strcmp(s1, "aggtabstate")){
1924 		    int   n;
1925 		    char *p;
1926 
1927 		    if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
1928 			set_variable(V_WP_AGGSTATE, p = int2string(n), 0, 0, Main);
1929 			Tcl_SetResult(interp, p, TCL_VOLATILE);
1930 		    }
1931 		    return(TCL_OK);
1932 		}
1933 		else if(!strcmp(s1, "alpinestate")){
1934                     char *wps, *p, *q, *twps = NULL;
1935                     int  dollars = 0;
1936 
1937                     if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1938                         for(p = wps; *p; p++)
1939                           if(*p == '$')
1940                             dollars++;
1941 
1942                         if(dollars){
1943                             twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char));
1944                             p = wps;
1945                             q = twps;
1946                             do{
1947                                 if(*p == '$')
1948                                   *q++ = '\\';
1949                             }
1950 			    while((*q++ = *p++) != '\0');
1951                         }
1952 
1953                         set_variable(V_WP_STATE, twps ? twps : wps, 0, 1, Main);
1954                         Tcl_SetResult(interp, wps, TCL_VOLATILE);
1955                         if(twps)
1956                           fs_give((void **) &twps);
1957                     }
1958 
1959 		    return(TCL_OK);
1960 		}
1961 		else if(!strcmp(s1, "set")){
1962 		    Tcl_Obj *rObj;
1963 
1964 		    if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
1965 			Tcl_SetObjResult(interp, rObj);
1966 			return(TCL_OK);
1967 		    }
1968 		    else
1969 		      return(TCL_ERROR);
1970 		}
1971 		else if(!strcmp(s1, "unset")){
1972 		    char *varname;
1973 
1974 		    return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR);
1975 		}
1976 	    }
1977 	    else if(objc == 4){
1978 		if(!strcmp(s1, "feature")){
1979 		    char      *featurename;
1980 		    int	       i, set, wasset = 0;
1981 		    FEATURE_S *feature;
1982 
1983 		    /*
1984 		     * CMD: feature
1985 		     *
1986 		     * ARGS: featurename -
1987 		     *	     value - new value to assign flag
1988 		     *
1989 		     * Returns: 1 if named feature set, 0 otherwise
1990 		     *
1991 		     */
1992 		    if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
1993 		       && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
1994 		      for(i = 0; (feature = feature_list(i)); i++)
1995 			if(!strucmp(featurename, feature->name)){
1996 			    if(set != F_ON(feature->id, wps_global)){
1997 				toggle_feature(wps_global,
1998 					       &wps_global->vars[V_FEATURE_LIST],
1999 					       feature, TRUE, Main);
2000 
2001 				if(wps_global->prc)
2002 				  wps_global->prc->outstanding_pinerc_changes = 1;
2003 			    }
2004 
2005 			    break;
2006 			}
2007 
2008 		    Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
2009 		    return(TCL_OK);
2010 		}
2011 		else if(!strucmp(s1, "help")){
2012 		    HelpType   text;
2013 		    int        i;
2014 		    char     **help_text, **ptext, *helpname, tmperrmsg[256],
2015 		              *function;
2016 		    Tcl_Obj   *itemObj;
2017 		    struct     variable *vtmp;
2018 		    FEATURE_S *ftmp;
2019 
2020 		    if(!(helpname = Tcl_GetStringFromObj(objv[2], NULL))){
2021 		      Tcl_SetResult(interp,
2022 				    "Can't Tcl_GetStringFromObj for helpname",
2023 				    TCL_VOLATILE);
2024 		      return(TCL_ERROR);
2025 		    }
2026 		    if(!(function = Tcl_GetStringFromObj(objv[3], NULL))){
2027 		      Tcl_SetResult(interp,
2028 				    "Can't Tcl_GetStringFromObj for function",
2029 				    TCL_VOLATILE);
2030 		      return(TCL_ERROR);
2031 		    }
2032 		    if(strucmp(function, "plain") == 0){
2033 		      if((text = help_name2section(helpname, strlen(helpname)))
2034 			 == NO_HELP)
2035 			return(TCL_OK);
2036 		    }
2037 		    else if(strucmp(function, "variable") == 0){
2038 		        for(vtmp = wps_global->vars;
2039 			    vtmp->name && strucmp(vtmp->name, helpname);
2040 			    vtmp++);
2041 			if(!vtmp->name) {
2042 			  snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
2043 				  strlen(helpname) < 200 ? helpname : "");
2044 			  Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
2045 			  return(TCL_ERROR);
2046 			}
2047 			text = config_help(vtmp - wps_global->vars, 0);
2048 			if(text == NO_HELP)
2049 			  return(TCL_OK);
2050 		    }
2051 		    else if(strucmp(function, "feature") == 0){
2052 		        for(i = 0; (ftmp = feature_list(i)); i++){
2053 			    if(!strucmp(helpname, ftmp->name)){
2054 			      text = ftmp->help;
2055 			      break;
2056 			    }
2057 			}
2058 			if(!ftmp || text == NO_HELP){
2059 			  return(TCL_OK);
2060 			}
2061 		    }
2062 		    else {
2063 		      snprintf(tmperrmsg, sizeof(tmperrmsg), "Invalid function: %s",
2064 			      strlen(helpname) < 200 ? function : "");
2065 		      Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
2066 		      return(TCL_ERROR);
2067 		    }
2068 		    /* assumption here is that HelpType is char **  */
2069 		    help_text = text;
2070 		    for(ptext = help_text; *ptext; ptext++){
2071 		      itemObj = Tcl_NewStringObj(*ptext, -1);
2072 		      Tcl_ListObjAppendElement(interp,
2073 					       Tcl_GetObjResult(interp),
2074 					       itemObj);
2075 		    }
2076 		    return(TCL_OK);
2077 		}
2078 		else if(!strcmp(s1, "varset")){
2079 		    char     *varname, **tmpstrlist, *line;
2080 		    struct    variable *vtmp;
2081 		    Tcl_Obj **objVal;
2082 		    int       i, numlistvals = 0, strlistpos;
2083 
2084 		    if((varname = Tcl_GetStringFromObj(objv[2], NULL))
2085 		       && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
2086 							   &objVal) == TCL_OK)){
2087 		        for(vtmp = wps_global->vars;
2088 			    vtmp->name && strucmp(vtmp->name, varname);
2089 			    vtmp++);
2090 			if(!vtmp->name){
2091 			  return(TCL_ERROR);
2092 			}
2093 			else{
2094 			    /* found the variable */
2095 			    if(vtmp->is_list){
2096 				for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
2097 				  fs_give((void **)&vtmp->main_user_val.l[i]);
2098 				if(vtmp->main_user_val.l)
2099 				  fs_give((void **)&vtmp->main_user_val.l);
2100 				if(numlistvals > 0){
2101 				    tmpstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
2102 				    for(i = 0, strlistpos = 0; i < numlistvals; i++){
2103 				        if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
2104 					  removing_leading_and_trailing_white_space(line);
2105 					  if(*line)
2106 					    tmpstrlist[strlistpos++] = cpystr(line);
2107 					}
2108 				    }
2109 				    tmpstrlist[strlistpos] = NULL;
2110 				    vtmp->main_user_val.l = (char **)fs_get((strlistpos+1) *
2111 									    sizeof(char *));
2112 				    for(i = 0; i <= strlistpos; i++)
2113 				      vtmp->main_user_val.l[i] = tmpstrlist[i];
2114 				    fs_give((void **)&tmpstrlist);
2115 				}
2116 				set_current_val(vtmp, FALSE, FALSE);
2117 				return(TCL_OK);
2118 			    }
2119 			    else{
2120 			        if((line = Tcl_GetStringFromObj(objVal[0], NULL)) != NULL){
2121 				    if(strucmp(vtmp->name, "reply-indent-string"))
2122 				      removing_leading_and_trailing_white_space(line);
2123 				    if(vtmp->main_user_val.p)
2124 				      fs_give((void **)&vtmp->main_user_val.p);
2125 				    if(*line)
2126 				      vtmp->main_user_val.p = cpystr(line);
2127 				    set_current_val(vtmp, FALSE, FALSE);
2128 				    return(TCL_OK);
2129 				}
2130 			    }
2131 			}
2132 		    }
2133 		    return(TCL_ERROR);
2134 		}
2135 		else if(!strcmp(s1, "mode")){
2136 		    char *mode;
2137 		    int	  value, rv = 0;
2138 
2139 		    /*
2140 		     * CMD: mode
2141 		     *
2142 		     * ARGS:  <mode> <value>
2143 		     *
2144 		     * Returns: old value of binary mode we were told to set
2145 		     *
2146 		     */
2147 		    if((mode = Tcl_GetStringFromObj(objv[2], NULL))
2148 		       && Tcl_GetIntFromObj(interp, objv[3], &value) != TCL_ERROR){
2149 			if(!strcmp(mode, "full-header-mode")){
2150 			    rv = wps_global->full_header;
2151 			    wps_global->full_header = value;
2152 			}
2153 		    }
2154 
2155 		    Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
2156 		    return(TCL_OK);
2157 		}
2158 		else if(!strcmp(s1, "set")){
2159 		    Tcl_Obj *rObj;
2160 
2161 		    if((rObj = Tcl_ObjSetVar2(interp, objv[2], NULL, objv[3], TCL_LEAVE_ERR_MSG)) != NULL){
2162 			Tcl_SetObjResult(interp, rObj);
2163 			return(TCL_OK);
2164 		    }
2165 		    else
2166 		      return(TCL_ERROR);
2167 		}
2168 	    }
2169 	    else
2170 	      err = "PEInfo: Too many arguments";
2171 	}
2172     }
2173 
2174     Tcl_SetResult(interp, err, TCL_STATIC);
2175     return(TCL_ERROR);
2176 }
2177 
2178 
2179 /*
2180  * PEConfigCmd - edit various alpine config variables
2181  *
2182  * The goal here is to remember what's changed, but not write to pinerc
2183  * until the user's actually chosen to save.
2184  */
2185 int
PEConfigCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2186 PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2187 {
2188     char *err = "Unknown PEConfig request";
2189     char *s1;
2190 
2191     dprint((2, "PEConfigCmd"));
2192 
2193     if(objc == 1){
2194 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
2195 	Tcl_SetResult(interp, err, TCL_STATIC);
2196 	return(TCL_ERROR);
2197     }
2198     s1 = Tcl_GetStringFromObj(objv[1], NULL);
2199 
2200     if(s1){
2201 	if(!strcmp(s1, "colorset")){
2202 	    char *varname, *fghex, *bghex;
2203 	    char  tvname[256], asciicolor[256];
2204 	    struct  variable *vtmp;
2205 	    Tcl_Obj **cObj;
2206 	    int       cObjc;
2207 	    SPEC_COLOR_S *hcolors, *thc;
2208 
2209 	    if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
2210 		Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
2211 		return(TCL_ERROR);
2212 	    }
2213 
2214 	    if(!strcmp(varname, "viewer-hdr-colors")){
2215 		char *newhdr = NULL, *newpat = NULL, *utype;
2216 		int   hindex, i;
2217 
2218 		if(objc < 5){
2219 		    Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
2220 		    return(TCL_ERROR);
2221 		}
2222 
2223 		if(wps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
2224 		  hcolors = spec_colors_from_varlist(wps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
2225 		else
2226 		  hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
2227 		if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
2228 		    Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
2229 		    return(TCL_ERROR);
2230 		}
2231 
2232 		if(!strcmp(utype, "delete")){
2233 		    if(!hcolors){
2234 			Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
2235 			return(TCL_ERROR);
2236 		    }
2237 
2238 		    if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
2239 			Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
2240 			return(TCL_ERROR);
2241 		    }
2242 
2243 		    if(hindex == 0){
2244 			thc = hcolors;
2245 			hcolors = hcolors->next;
2246 			thc->next = NULL;
2247 			free_spec_colors(&thc);
2248 		    }
2249 		    else{
2250 			/* zero based */
2251 			for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
2252 			  ;
2253 
2254 			if(thc && thc->next){
2255 			    SPEC_COLOR_S *thc2 = thc->next;
2256 
2257 			    thc->next = thc2->next;
2258 			    thc2->next = NULL;
2259 			    free_spec_colors(&thc2);
2260 			}
2261 			else{
2262 			    Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
2263 			    return(TCL_ERROR);
2264 			}
2265 		    }
2266 		}
2267 		else if(!strcmp(utype, "add")){
2268 		    if(objc != 6){
2269 			Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
2270 			return(TCL_ERROR);
2271 		    }
2272 
2273 		    if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
2274 		      return (TCL_ERROR);
2275 
2276 		    if(cObjc != 2){
2277 			Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
2278 			return(TCL_ERROR);
2279 		    }
2280 
2281 		    newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
2282 		    newpat = Tcl_GetStringFromObj(cObj[1], NULL);
2283 		    if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
2284 		      return (TCL_ERROR);
2285 
2286 		    if(cObjc != 2){
2287 			Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
2288 			return(TCL_ERROR);
2289 		    }
2290 
2291 		    fghex = Tcl_GetStringFromObj(cObj[0], NULL);
2292 		    bghex = Tcl_GetStringFromObj(cObj[1], NULL);
2293 		    if(newhdr && newpat && fghex && bghex){
2294 			SPEC_COLOR_S **hcp;
2295 
2296 			for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
2297 			  ;
2298 
2299 			*hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
2300 			(*hcp)->inherit = 0;
2301 			(*hcp)->spec = cpystr(newhdr);
2302 			(*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
2303 			(*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
2304 
2305 			if(newpat && *newpat)
2306 			  (*hcp)->val = string_to_pattern(newpat);
2307 			else
2308 			  (*hcp)->val = NULL;
2309 
2310 			(*hcp)->next = NULL;
2311 		    }
2312 		    else{
2313 			Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
2314 			return(TCL_ERROR);
2315 		    }
2316 		}
2317 		else if(!strcmp(utype, "update")){
2318 		    if(objc != 6){
2319 			Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
2320 			return(TCL_ERROR);
2321 		    }
2322 
2323 		    if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
2324 			 && cObjc == 3
2325 			 && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
2326 			 && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
2327 			 && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
2328 			Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
2329 			return (TCL_ERROR);
2330 		    }
2331 
2332 		    if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
2333 			 && cObjc == 2
2334 			 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
2335 			 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
2336 			Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
2337 			return (TCL_ERROR);
2338 		    }
2339 
2340 		    for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
2341 		      ;
2342 
2343 		    if(!thc){
2344 			Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
2345 			return (TCL_ERROR);
2346 		    }
2347 
2348 		    if(thc->spec)
2349 		      fs_give((void **)&thc->spec);
2350 
2351 		    thc->spec = cpystr(newhdr);
2352 		    if(ascii_colorstr(asciicolor, fghex) == 0) {
2353 			if(thc->fg)
2354 			  fs_give((void **)&thc->fg);
2355 
2356 			thc->fg = cpystr(asciicolor);
2357 		    }
2358 		    else{
2359 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
2360 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2361 			return(TCL_ERROR);
2362 		    }
2363 
2364 		    if(ascii_colorstr(asciicolor, bghex) == 0) {
2365 			if(thc->bg)
2366 			  fs_give((void **)&thc->bg);
2367 
2368 			thc->bg = cpystr(asciicolor);
2369 		    }
2370 		    else{
2371 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2372 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2373 			return(TCL_ERROR);
2374 		    }
2375 
2376 		    if(thc->val)
2377 		      fs_give((void **)&thc->val);
2378 
2379 		    if(newpat && *newpat){
2380 			thc->val = string_to_pattern(newpat);
2381 		    }
2382 		}
2383 		else{
2384 		    Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
2385 		    return(TCL_ERROR);
2386 		}
2387 
2388 		vtmp = &wps_global->vars[V_VIEW_HDR_COLORS];
2389 		for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
2390 		  fs_give((void **)&vtmp->changed_val.l[i]);
2391 
2392 		if(vtmp->changed_val.l)
2393 		  fs_give((void **)&vtmp->changed_val.l);
2394 
2395 		vtmp->changed_val.l = varlist_from_spec_colors(hcolors);
2396 		vtmp->is_changed_val = 1;
2397 		free_spec_colors(&hcolors);
2398 		return(TCL_OK);
2399 	    }
2400 	    else {
2401 		if(objc != 4){
2402 		    Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
2403 		    return(TCL_ERROR);
2404 		}
2405 
2406 		if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
2407 		     && cObjc == 2
2408 		     && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
2409 		     && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
2410 		    Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
2411 		    return (TCL_ERROR);
2412 		}
2413 
2414 		snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
2415 		for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
2416 		    vtmp->name && strucmp(vtmp->name, tvname);
2417 		    vtmp++)
2418 		  ;
2419 
2420 		if(!vtmp->name || vtmp->is_list){
2421 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2422 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2423 		    return(TCL_ERROR);
2424 		}
2425 
2426 		if(ascii_colorstr(asciicolor, fghex) == 0) {
2427 		    if(vtmp->changed_val.p)
2428 		      fs_give((void **)&vtmp->changed_val.p);
2429 
2430 		    vtmp->changed_val.p = cpystr(asciicolor);
2431 		    vtmp->is_changed_val = 1;
2432 
2433 		    /* We need to handle this in the actual config setting
2434 		     *  if(!strucmp(varname, "normal"))
2435 		     *  pico_set_fg_color(asciicolor);
2436 		     */
2437 		}
2438 		else{
2439 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
2440 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2441 		    return(TCL_ERROR);
2442 		}
2443 
2444 		snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
2445 		vtmp++;
2446 		if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
2447 		  for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
2448 		      vtmp->name && strucmp(vtmp->name, tvname);
2449 		      vtmp++)
2450 		    ;
2451 
2452 		if(!vtmp->name || vtmp->is_list){
2453 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2454 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2455 		    return(TCL_ERROR);
2456 		}
2457 
2458 		if(ascii_colorstr(asciicolor, bghex) == 0) {
2459 		    if(vtmp->changed_val.p)
2460 		      fs_give((void **)&vtmp->changed_val.p);
2461 
2462 		    vtmp->changed_val.p = cpystr(asciicolor);
2463 		    vtmp->is_changed_val = 1;
2464 		    /* again, we need to handle this when we actually set the variable
2465 		     * if(!strucmp(varname, "normal"))
2466 		     *  pico_set_bg_color(asciicolor);
2467 		     */
2468 		}
2469 		else{
2470 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2471 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2472 		    return(TCL_ERROR);
2473 		}
2474 
2475 		Tcl_SetResult(interp, "1", TCL_STATIC);
2476 		return(TCL_OK);
2477 	    }
2478 	}
2479 	else if(!strcmp(s1, "ruleset")){
2480 	    return(peRuleSet(interp, &((Tcl_Obj **)objv)[2]));
2481 	}
2482 	else if(objc == 2){
2483 	    if(!strcmp(s1, "featuresettings")){
2484 		struct variable *vtmp;
2485 		int i;
2486 		FEATURE_S *feature;
2487 
2488 		vtmp = &wps_global->vars[V_FEATURE_LIST];
2489 		for(i = 0; (feature = feature_list(i)); i++)
2490 		  if(feature_list_section(feature)){
2491 		      if(vtmp->is_changed_val ? F_CH_ON(feature->id)
2492 			 : F_ON(feature->id, wps_global)){
2493 			  Tcl_ListObjAppendElement(interp,
2494 						   Tcl_GetObjResult(interp),
2495 						   Tcl_NewStringObj(feature->name, -1));
2496 		      }
2497 		  }
2498 		return(TCL_OK);
2499 	    }
2500 	    else if(!strcmp(s1, "rawsig")){
2501 		char *err = NULL, *sig = NULL, *p, *q;
2502 		int i;
2503 		struct variable *vtmp;
2504 
2505 		vtmp = &wps_global->vars[V_LITERAL_SIG];
2506 		if(vtmp->is_changed_val ? vtmp->changed_val.p
2507 		   : wps_global->VAR_LITERAL_SIG){
2508 		    char     *err = NULL;
2509 		    char    **apval;
2510 
2511 		    if(wps_global->restricted){
2512 			err = "Alpine demo can't change config file";
2513 		    }
2514 		    else{
2515 			/* BUG: no "exceptions file" support */
2516 			apval = (vtmp->is_changed_val ? &vtmp->changed_val.p
2517 				 : APVAL(&wps_global->vars[V_LITERAL_SIG], Main));
2518 			if(apval){
2519 			    sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
2520 			    sig[0] = '\0';
2521 			    cstring_to_string(*apval, sig);
2522 			}
2523 			else
2524 			  err = "Problem accessing configuration";
2525 		    }
2526 		}
2527 		else if((vtmp = &wps_global->vars[V_SIGNATURE_FILE])
2528 			&& !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p
2529 				      : wps_global->VAR_SIGNATURE_FILE))
2530 		  snprintf(err = wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
2531 			  vtmp->is_changed_val ? (vtmp->changed_val.p
2532 						  ? vtmp->changed_val.p : "<null>")
2533 			  : (wps_global->VAR_SIGNATURE_FILE
2534 			     ? wps_global->VAR_SIGNATURE_FILE : "<null>"));
2535 		else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val
2536 						    ? vtmp->changed_val.p
2537 						    : wps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))))
2538 		  err = "Can't read remote pinerc";
2539 
2540 		if(err){
2541 		    Tcl_SetResult(interp, err, TCL_VOLATILE);
2542 		    return(TCL_ERROR);
2543 		}
2544 
2545 		if(peTSig){
2546 		    for(i = 0; peTSig[i]; i++)
2547 		      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2548 					       Tcl_NewStringObj(peTSig[i],-1));
2549 		}
2550 		else {
2551 		    for(p = sig; (q = strindex(p, '\n')); p = q + 1)
2552 		      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2553 					       Tcl_NewStringObj(p, q - p));
2554 
2555 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2556 					     Tcl_NewStringObj(p, -1));
2557 		    fs_give((void **) &sig);
2558 		}
2559 		return(TCL_OK);
2560 	    }
2561 	    else if(!strcmp(s1, "filters")){
2562 		long      rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
2563 		PAT_STATE pstate;
2564 		PAT_S    *pat;
2565 
2566 		close_every_pattern();
2567 		if(any_patterns(rflags, &pstate)){
2568 		    for(pat = first_pattern(&pstate);
2569 			pat;
2570 			pat = next_pattern(&pstate)){
2571 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2572 						 Tcl_NewStringObj(pat->patgrp->nick, -1));
2573 		    }
2574 		}
2575 		return(TCL_OK);
2576 	    }
2577 	    else if(!strcmp(s1, "scores")){
2578 		long      rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
2579 		PAT_STATE pstate;
2580 		PAT_S    *pat;
2581 
2582 		close_every_pattern();
2583 		if(any_patterns(rflags, &pstate)){
2584 		    for(pat = first_pattern(&pstate);
2585 			pat;
2586 			pat = next_pattern(&pstate)){
2587 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2588 						 Tcl_NewStringObj(pat->patgrp->nick, -1));
2589 		    }
2590 		}
2591 		return(TCL_OK);
2592 	    }
2593 	    else if(!strcmp(s1, "indexcolors")){
2594 		long      rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
2595 		PAT_STATE pstate;
2596 		PAT_S    *pat;
2597 
2598 		close_every_pattern();
2599 		if(any_patterns(rflags, &pstate)){
2600 		    for(pat = first_pattern(&pstate);
2601 			pat;
2602 			pat = next_pattern(&pstate)){
2603 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2604 						 Tcl_NewStringObj(pat->patgrp->nick, -1));
2605 		    }
2606 		}
2607 		return(TCL_OK);
2608 	    }
2609 	    else if(!strcmp(s1, "collections")){
2610 		struct variable  *vtmp;
2611 		int               i;
2612 		CONTEXT_S        *new_ctxt;
2613 
2614 		vtmp = &wps_global->vars[V_FOLDER_SPEC];
2615 		for(i = 0; (vtmp->is_changed_val
2616 			    ? vtmp->changed_val.l && vtmp->changed_val.l[i]
2617 			    : vtmp->current_val.l && vtmp->current_val.l[i]);
2618 		    i++){
2619 		    new_ctxt = new_context(vtmp->is_changed_val
2620 					   ? vtmp->changed_val.l[i]
2621 					   : vtmp->current_val.l[i], NULL);
2622 		    peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
2623 			       new_ctxt->nickname
2624 			       ? new_ctxt->nickname
2625 			       : (new_ctxt->server
2626 				  ? new_ctxt->server
2627 				  : (new_ctxt->label
2628 				     ? new_ctxt->label
2629 				     : "Some Collection")),
2630 			       new_ctxt->label ? new_ctxt->label : "");
2631 		    free_context(&new_ctxt);
2632 		}
2633 		vtmp = &wps_global->vars[V_NEWS_SPEC];
2634 		for(i = 0; (vtmp->is_changed_val
2635 			    ? vtmp->changed_val.l && vtmp->changed_val.l[i]
2636 			    : vtmp->current_val.l && vtmp->current_val.l[i]);
2637 		    i++){
2638 		    new_ctxt = new_context(vtmp->is_changed_val
2639 					   ? vtmp->changed_val.l[i]
2640 					   : vtmp->current_val.l[i], NULL);
2641 		    peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
2642 			       new_ctxt->nickname
2643 			       ? new_ctxt->nickname
2644 			       : (new_ctxt->server
2645 				  ? new_ctxt->server
2646 				  : (new_ctxt->label
2647 				     ? new_ctxt->label
2648 				     : "Some Collection")),
2649 				    new_ctxt->label ? new_ctxt->label : "");
2650 		    free_context(&new_ctxt);
2651 		}
2652 
2653 		return(TCL_OK);
2654 	    }
2655 	    else if(!strcmp(s1, "newconf")){
2656 		struct variable *vtmp;
2657 		int i;
2658 		FEATURE_S *feature;
2659 
2660 		for(vtmp = wps_global->vars; vtmp->name; vtmp++)
2661 		  vtmp->is_changed_val = 0;
2662 
2663 		for(i = 0; (feature = feature_list(i)); i++)
2664 		  F_CH_SET(feature->id, F_ON(feature->id, wps_global));
2665 
2666 		if(peTSig){
2667 		    for(i = 0; peTSig[i]; i++)
2668 		      fs_give((void **)&peTSig[i]);
2669 		    fs_give((void **)&peTSig);
2670 		}
2671 
2672 		close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED);
2673 		return(TCL_OK);
2674 	    }
2675 	    else if(!strcmp(s1, "saveconf")){
2676 		struct variable *vtmp;
2677 		int              i, did_change = 0, def_sort_rev;
2678 		FEATURE_S       *feature;
2679 
2680 		if(wps_global->vars[V_FEATURE_LIST].is_changed_val){
2681 		    wps_global->vars[V_FEATURE_LIST].is_changed_val = 0;
2682 		    for(i = 0; (feature = feature_list(i)); i++)
2683 		      if(feature_list_section(feature)){
2684 			  if(F_CH_ON(feature->id) != F_ON(feature->id, wps_global)){
2685 			      did_change = 1;
2686 			      toggle_feature(wps_global,
2687 					     &wps_global->vars[V_FEATURE_LIST],
2688 					     feature, TRUE, Main);
2689 			  }
2690 		      }
2691 		}
2692 
2693 		for(vtmp = wps_global->vars; vtmp->name; vtmp++){
2694 		    if(vtmp->is_changed_val
2695 			&& (vtmp - wps_global->vars != V_FEATURE_LIST)){
2696 			if(vtmp->is_list){
2697 			    for(i = 0; vtmp->main_user_val.l
2698 				  && vtmp->main_user_val.l[i]; i++)
2699 			      fs_give((void **)&vtmp->main_user_val.l[i]);
2700 			    if(vtmp->main_user_val.l)
2701 			      fs_give((void **)&vtmp->main_user_val.l);
2702 			    vtmp->main_user_val.l = vtmp->changed_val.l;
2703 			    vtmp->changed_val.l = NULL;
2704 			}
2705 			else {
2706 			    if(vtmp->main_user_val.p)
2707 			      fs_give((void **)&vtmp->main_user_val.p);
2708 			    vtmp->main_user_val.p = vtmp->changed_val.p;
2709 			    vtmp->changed_val.p = NULL;
2710 			}
2711 			set_current_val(vtmp, FALSE, FALSE);
2712 			vtmp->is_changed_val = 0;
2713 			did_change = 1;
2714 			switch (vtmp - wps_global->vars) {
2715 			    case V_USER_DOMAIN:
2716 			      init_hostname(wps_global);
2717 			    case V_FOLDER_SPEC:
2718 			    case V_NEWS_SPEC:
2719 			      free_contexts(&wps_global->context_list);
2720 			      init_folders(wps_global);
2721 			      break;
2722 			    case V_NORM_FORE_COLOR:
2723 			      pico_set_fg_color(vtmp->current_val.p);
2724 			      break;
2725 			    case V_NORM_BACK_COLOR:
2726 			      pico_set_bg_color(vtmp->current_val.p);
2727 			      break;
2728 			    case V_ADDRESSBOOK:
2729 			    case V_GLOB_ADDRBOOK:
2730 #ifdef	ENABLE_LDAP
2731 			    case V_LDAP_SERVERS:
2732 #endif
2733 			    case V_ABOOK_FORMATS:
2734 			      addrbook_reset();
2735 			    case V_INDEX_FORMAT:
2736 			      init_index_format(wps_global->VAR_INDEX_FORMAT,
2737 						&wps_global->index_disp_format);
2738 			      clear_index_cache(sp_inbox_stream(), 0);
2739 			      break;
2740 			    case V_PAT_FILTS:
2741 			      close_patterns(ROLE_DO_FILTER | PAT_USE_CURRENT);
2742 			      role_process_filters();
2743 			      break;
2744 			    case V_PAT_INCOLS:
2745 			      close_patterns(ROLE_DO_INCOLS | PAT_USE_CURRENT);
2746 			      clear_index_cache(sp_inbox_stream(), 0);
2747 			      role_process_filters();
2748 			      break;
2749 			    case V_PAT_SCORES:
2750 			      close_patterns(ROLE_DO_SCORES | PAT_USE_CURRENT);
2751 			      role_process_filters();
2752 			      break;
2753 			    case V_DEFAULT_FCC:
2754 			    case V_DEFAULT_SAVE_FOLDER:
2755 			      init_save_defaults();
2756 			      break;
2757 			    case V_SORT_KEY:
2758 			      decode_sort(wps_global->VAR_SORT_KEY, &wps_global->def_sort, &def_sort_rev);
2759 			      break;
2760 			    case V_VIEW_HDR_COLORS :
2761 			      set_custom_spec_colors(wps_global);
2762 			      break;
2763 			    case V_POST_CHAR_SET :
2764 			      update_posting_charset(wps_global, 1);
2765 			      break;
2766 			    default:
2767 			      break;
2768 			}
2769 		    }
2770 		}
2771 		if(peTSig){
2772 		    peWriteSig(interp, wps_global->VAR_SIGNATURE_FILE, NULL);
2773 		}
2774 		if(did_change){
2775 		    if(write_pinerc(wps_global, Main, WRP_NOUSER) == 0)
2776 		      q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!");
2777 		}
2778 		return(TCL_OK);
2779 	    }
2780 	    else if(!strcmp(s1, "columns")){
2781 		Tcl_SetResult(interp, int2string(wps_global->ttyo->screen_cols), TCL_VOLATILE);
2782 		return(TCL_OK);
2783 	    }
2784 	    else if(!strcmp(s1, "indextokens")){
2785 		INDEX_PARSE_T *tok;
2786 		int	       i;
2787 
2788 		for(i = 0; (tok = itoken(i)) != NULL; i++)
2789 		  if(tok->what_for & FOR_INDEX)
2790 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2791 					     Tcl_NewStringObj(tok->name, -1));
2792 
2793 		return(TCL_OK);
2794 	    }
2795 	}
2796 	else if(objc == 3){
2797 	    if(!strcmp(s1, "varget")){
2798 		char *varname = Tcl_GetStringFromObj(objv[2], NULL);
2799 		struct variable *vtmp;
2800 		Tcl_Obj         *resObj, *secObj;
2801 		char            *input_type;
2802 		int              is_default, i;
2803 		NAMEVAL_S       *tmpnv;
2804 
2805 		if(varname == NULL) return(TCL_ERROR);
2806 
2807 		for(vtmp = wps_global->vars;
2808 		    vtmp->name && strucmp(vtmp->name, varname);
2809 		    vtmp++)
2810 		  ;
2811 
2812 		if(!vtmp->name){
2813 		    Tcl_SetResult(interp, err, TCL_VOLATILE);
2814 		    return(TCL_ERROR);
2815 		}
2816 		resObj = Tcl_NewListObj(0, NULL);
2817 		if(vtmp->is_list){
2818 		    if(vtmp->is_changed_val){
2819 			for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++){
2820 			    Tcl_ListObjAppendElement(interp, resObj,
2821 					Tcl_NewStringObj(vtmp->changed_val.l[i], -1));
2822 			}
2823 		    }
2824 		    else {
2825 			for(i = 0; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
2826 			    Tcl_ListObjAppendElement(interp, resObj,
2827 					Tcl_NewStringObj(vtmp->current_val.l[i], -1));
2828 			}
2829 		    }
2830 		}
2831 		else {
2832 		    if(vtmp->is_changed_val){
2833 			if(vtmp->changed_val.p)
2834 			  Tcl_ListObjAppendElement(interp, resObj,
2835 						   Tcl_NewStringObj(vtmp->changed_val.p[0]
2836 								    ? vtmp->changed_val.p
2837 								    : "\"\"", -1));
2838 		    }
2839 		    else {
2840 			if(vtmp->current_val.p)
2841 			  Tcl_ListObjAppendElement(interp, resObj,
2842 						   Tcl_NewStringObj(vtmp->current_val.p[0]
2843 								    ? vtmp->current_val.p
2844 								    : "\"\"", -1));
2845 		    }
2846 		}
2847 		Tcl_ListObjAppendElement(interp,
2848 					 Tcl_GetObjResult(interp),
2849 					 resObj);
2850 		secObj = Tcl_NewListObj(0, NULL);
2851 		if(vtmp->is_list)
2852 		  input_type = cpystr("textarea");
2853 		else{
2854 		    NAMEVAL_S *(*tmpf)(int);
2855 		    switch(vtmp - wps_global->vars){
2856 		      case V_SAVED_MSG_NAME_RULE:
2857 			tmpf = save_msg_rules;
2858 			break;
2859 		      case V_FCC_RULE:
2860 			tmpf = fcc_rules;
2861 			break;
2862 		      case V_SORT_KEY:
2863 			tmpf = sort_key_rules;
2864 			break;
2865 		      case V_AB_SORT_RULE:
2866 			tmpf = ab_sort_rules;
2867 			break;
2868 		      case V_FLD_SORT_RULE:
2869 			tmpf = fld_sort_rules;
2870 			break;
2871 		      case V_GOTO_DEFAULT_RULE:
2872 			tmpf = goto_rules;
2873 			break;
2874 		      case V_INCOMING_STARTUP:
2875 			tmpf = incoming_startup_rules;
2876 			break;
2877 		      case V_PRUNING_RULE:
2878 			tmpf = pruning_rules;
2879 			break;
2880 		      case V_WP_INDEXHEIGHT:
2881 			tmpf = wp_indexheight_rules;
2882 			break;
2883 		      default:
2884 			tmpf = NULL;
2885 			break;
2886 		    }
2887 		    if(tmpf){
2888 			for(i = 0; (tmpnv = (tmpf)(i)); i++){
2889 			  if(tmpnv->shortname)
2890 			    peAppListF(interp, secObj, "%s%s", tmpnv->name, tmpnv->shortname);
2891 			  else
2892 			    Tcl_ListObjAppendElement(interp, secObj,
2893 						     Tcl_NewStringObj(tmpnv->name, -1));
2894 			}
2895 			input_type = cpystr("listbox");
2896 		    }
2897 		    else
2898 		      input_type = cpystr("text");
2899 		}
2900 		Tcl_ListObjAppendElement(interp,
2901 					 Tcl_GetObjResult(interp),
2902 					 Tcl_NewStringObj(input_type, -1));
2903 		Tcl_ListObjAppendElement(interp,
2904 					 Tcl_GetObjResult(interp),
2905 					 secObj);
2906 		if(vtmp->is_list)
2907 		  is_default = !vtmp->is_changed_val && !vtmp->main_user_val.l;
2908 		else
2909 		  is_default = !vtmp->is_changed_val && !vtmp->main_user_val.p;
2910 		Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2911 					 Tcl_NewIntObj(is_default));
2912 		Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2913 					 Tcl_NewIntObj(vtmp->is_fixed));
2914 		return(TCL_OK);
2915 	    }
2916 	    else if(!strcmp(s1, "filtextended")){
2917 		int fl, i;
2918 		long      rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
2919 		PAT_STATE pstate;
2920 		PAT_S    *pat;
2921 		Tcl_Obj  *resObj = NULL, *tObj = NULL;
2922 
2923 		if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2924 		  return(TCL_ERROR);
2925 
2926 		close_every_pattern();
2927 		if(any_patterns(rflags, &pstate)){
2928 		    for(pat = first_pattern(&pstate), i = 0;
2929 			pat && i != fl;
2930 			pat = next_pattern(&pstate), i++);
2931 
2932 		    if(!pat)
2933 		      return(TCL_ERROR);
2934 
2935 		    /* append the pattern ID */
2936 		    tObj = Tcl_NewListObj(0, NULL);
2937 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
2938 		    pePatAppendID(interp, tObj, pat);
2939 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2940 
2941 		    /* append the pattern */
2942 		    tObj = Tcl_NewListObj(0, NULL);
2943 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
2944 		    pePatAppendPattern(interp, tObj, pat);
2945 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2946 
2947 		    /* now append the filter action */
2948 		    resObj = Tcl_NewListObj(0, NULL);
2949 		    peAppListF(interp, resObj, "%s%i", "kill", pat->action->folder ? 0 : 1);
2950 		    peAppListF(interp, resObj, "%s%p", "folder", pat->action->folder);
2951 		    peAppListF(interp, resObj, "%s%i", "move_only_if_not_deleted",
2952 			       pat->action->move_only_if_not_deleted);
2953 		    tObj = Tcl_NewListObj(0, NULL);
2954 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("filtaction", -1));
2955 		    Tcl_ListObjAppendElement(interp, tObj, resObj);
2956 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2957 		}
2958 		else return(TCL_ERROR);
2959 
2960 		return(TCL_OK);
2961 	    }
2962 	    else if(!strcmp(s1, "indexcolorextended")){
2963 		int fl, i;
2964 		long      rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
2965 		PAT_STATE pstate;
2966 		PAT_S    *pat;
2967 		Tcl_Obj  *resObj = NULL, *tObj = NULL;
2968 
2969 		if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2970 		  return(TCL_ERROR);
2971 
2972 		close_every_pattern();
2973 		if(any_patterns(rflags, &pstate)){
2974 		    for(pat = first_pattern(&pstate), i = 0;
2975 			pat && i != fl;
2976 			pat = next_pattern(&pstate), i++);
2977 
2978 		    if(!pat)
2979 		      return(TCL_ERROR);
2980 
2981 		    /* append the pattern ID */
2982 		    tObj = Tcl_NewListObj(0, NULL);
2983 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
2984 		    pePatAppendID(interp, tObj, pat);
2985 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2986 
2987 		    /* append the pattern */
2988 		    tObj = Tcl_NewListObj(0, NULL);
2989 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
2990 		    pePatAppendPattern(interp, tObj, pat);
2991 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2992 
2993 		    /* now append the pattern colors */
2994 		    resObj = Tcl_NewListObj(0, NULL);
2995 		    tObj = Tcl_NewListObj(0, NULL);
2996 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolor", -1));
2997 		    if(pat->action->is_a_incol){
2998 			char    *color;
2999 			Tcl_Obj *colObj = Tcl_NewListObj(0, NULL);
3000 
3001 			if(!(pat->action->incol
3002 			     && pat->action->incol->fg
3003 			     && pat->action->incol->fg[0]
3004 			     && (color = color_to_asciirgb(pat->action->incol->fg))
3005 			     && (color = peColorStr(color,wtmp_20k_buf))))
3006 			  color = "";
3007 
3008 			Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
3009 
3010 			if(!(pat->action->incol
3011 			     && pat->action->incol->bg
3012 			     && pat->action->incol->bg[0]
3013 			     && (color = color_to_asciirgb(pat->action->incol->bg))
3014 			     && (color = peColorStr(color,wtmp_20k_buf))))
3015 			  color = "";
3016 
3017 			Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
3018 			Tcl_ListObjAppendElement(interp, tObj, colObj);
3019 		    }
3020 		    Tcl_ListObjAppendElement(interp, resObj, tObj);
3021 
3022 		    tObj = Tcl_NewListObj(0, NULL);
3023 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolors", -1));
3024 		    Tcl_ListObjAppendElement(interp, tObj, resObj);
3025 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3026 		}
3027 		else return(TCL_ERROR);
3028 
3029 		return(TCL_OK);
3030 	    }
3031 	    else if(!strcmp(s1, "scoreextended")){
3032 		int	    fl, i;
3033 		long	    rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
3034 		char	   *hdr = NULL;
3035 		PAT_STATE   pstate;
3036 		PAT_S	   *pat;
3037 		Tcl_Obj	   *resObj = NULL, *tObj = NULL;
3038 
3039 		if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
3040 		  return(TCL_ERROR);
3041 
3042 		close_every_pattern();
3043 		if(any_patterns(rflags, &pstate)){
3044 		    for(pat = first_pattern(&pstate), i = 0;
3045 			pat && i != fl;
3046 			pat = next_pattern(&pstate), i++);
3047 
3048 		    if(!pat)
3049 		      return(TCL_ERROR);
3050 
3051 		    /* append the pattern ID */
3052 		    tObj = Tcl_NewListObj(0, NULL);
3053 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
3054 		    pePatAppendID(interp, tObj, pat);
3055 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3056 
3057 		    /* append the pattern */
3058 		    tObj = Tcl_NewListObj(0, NULL);
3059 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
3060 		    pePatAppendPattern(interp, tObj, pat);
3061 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3062 
3063 		    /* now append the filter action */
3064 		    resObj = Tcl_NewListObj(0, NULL);
3065 		    peAppListF(interp, resObj, "%s%l", "scoreval", pat->action->scoreval);
3066 		    if(pat->action->scorevalhdrtok)
3067 		      hdr = hdrtok_to_stringform(pat->action->scorevalhdrtok);
3068 
3069 		    peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : "");
3070 
3071 		    if(hdr)
3072 		      fs_give((void **) &hdr);
3073 
3074 		    tObj = Tcl_NewListObj(0, NULL);
3075 		    Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("scores", -1));
3076 		    Tcl_ListObjAppendElement(interp, tObj, resObj);
3077 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3078 		}
3079 		else return(TCL_ERROR);
3080 
3081 		return(TCL_OK);
3082 	    }
3083 	    else if(!strcmp(s1, "clextended")){
3084 		int cl, i, j = 0, in_folder_spec = 0;
3085 		struct variable *vtmp;
3086 		char tpath[MAILTMPLEN], *p;
3087 		CONTEXT_S *ctxt;
3088 
3089 		vtmp = &wps_global->vars[V_FOLDER_SPEC];
3090 		if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3091 		  return(TCL_ERROR);
3092 		for(i = 0; i < cl && (vtmp->is_changed_val
3093 				      ? (vtmp->changed_val.l
3094 					  && vtmp->changed_val.l[i])
3095 				      : (vtmp->current_val.l
3096 					  && vtmp->current_val.l[i])); i++);
3097 		if(i == cl && (vtmp->is_changed_val
3098 			       ? vtmp->changed_val.l && vtmp->changed_val.l[i]
3099 			       : vtmp->current_val.l && vtmp->current_val.l[i]))
3100 		  in_folder_spec = 1;
3101 		else {
3102 		    vtmp = &wps_global->vars[V_NEWS_SPEC];
3103 		    for(j = 0; i + j < cl && (vtmp->is_changed_val
3104 					      ? (vtmp->changed_val.l
3105 						 && vtmp->changed_val.l[j])
3106 					      : (vtmp->current_val.l
3107 						 && vtmp->current_val.l[j])); j++);
3108 		}
3109 		if(in_folder_spec || (i + j == cl && (vtmp->is_changed_val
3110 			       ? vtmp->changed_val.l && vtmp->changed_val.l[j]
3111 			       : vtmp->current_val.l && vtmp->current_val.l[j]))){
3112 		    ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[in_folder_spec ? i : j]
3113 				       : vtmp->current_val.l[in_folder_spec ? i : j], NULL);
3114 		    Tcl_ListObjAppendElement(interp,
3115 					     Tcl_GetObjResult(interp),
3116 					     Tcl_NewStringObj(ctxt->nickname ? ctxt->nickname : "", -1));
3117 		    Tcl_ListObjAppendElement(interp,
3118 					     Tcl_GetObjResult(interp),
3119 					     Tcl_NewStringObj(ctxt->label ? ctxt->label : "", -1));
3120 		    Tcl_ListObjAppendElement(interp,
3121 					     Tcl_GetObjResult(interp),
3122 					     Tcl_NewStringObj(ctxt->server ? ctxt->server : "", -1));
3123 		    tpath[0] = '\0';
3124 		    if(ctxt->context){
3125 			strncpy(tpath, (ctxt->context[0] == '{'
3126 				       && (p = strchr(ctxt->context, '}')))
3127 			       ? ++p
3128 			       : ctxt->context, sizeof(tpath));
3129 			tpath[sizeof(tpath)-1] = '\0';
3130 			if((p = strstr(tpath, "%s")) != NULL)
3131 			  *p = '\0';
3132 		    }
3133 		    Tcl_ListObjAppendElement(interp,
3134 					     Tcl_GetObjResult(interp),
3135 					     Tcl_NewStringObj(tpath, -1));
3136 		    Tcl_ListObjAppendElement(interp,
3137 					     Tcl_GetObjResult(interp),
3138 					     Tcl_NewStringObj(ctxt->dir && ctxt->dir->view.user
3139 							      ? ctxt->dir->view.user : "", -1));
3140 		    free_context(&ctxt);
3141 
3142 		    return(TCL_OK);
3143 		}
3144 		else
3145 		  return(TCL_ERROR);
3146 	    }
3147 	    else if(!strcmp(s1, "rawsig")){
3148 		struct variable *vtmp;
3149 		char	 *cstring_version, *sig, *line;
3150 		int	  i, nSig;
3151 		Tcl_Obj **objSig;
3152 
3153 		vtmp = &wps_global->vars[V_LITERAL_SIG];
3154 		if(vtmp->is_changed_val ? vtmp->changed_val.p
3155 		   : wps_global->VAR_LITERAL_SIG){
3156 
3157 		    wtmp_20k_buf[0] = '\0';
3158 		    Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
3159 		    for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
3160 		      if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
3161 			snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
3162 
3163 		    sig = cpystr(wtmp_20k_buf);
3164 
3165 		    if((cstring_version = string_to_cstring(sig)) != NULL){
3166 			if(vtmp->changed_val.p)
3167 			  fs_give((void **)&vtmp->changed_val.p);
3168 			vtmp->is_changed_val = 1;
3169 			vtmp->changed_val.p = cstring_version;
3170 		    }
3171 
3172 		    fs_give((void **) &sig);
3173 		    return(TCL_OK);
3174 		}
3175 		else {
3176 		    if(peTSig){
3177 			for(i = 0; peTSig[i]; i++)
3178 			  fs_give((void **)&peTSig[i]);
3179 			fs_give((void **)&peTSig);
3180 		    }
3181 		    Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
3182 		    peTSig = (char **)fs_get(sizeof(char)*(nSig + 1));
3183 		    for(i = 0; i < nSig; i++){
3184 			line = Tcl_GetStringFromObj(objSig[i], NULL);
3185 			peTSig[i] = cpystr(line ? line : "");
3186 		    }
3187 		    peTSig[i] = NULL;
3188 		    return(TCL_OK);
3189 		}
3190 	    }
3191 	    else if(!strcmp(s1, "colorget")){
3192 		char             *varname;
3193 		char              tvname[256], hexcolor[256];
3194 		struct  variable *vtmp;
3195 		if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
3196 		    return(TCL_ERROR);
3197 		}
3198 		if(strcmp("viewer-hdr-colors", varname) == 0){
3199 		    SPEC_COLOR_S *hcolors, *thc;
3200 		    Tcl_Obj     *resObj;
3201 		    char         hexcolor[256], *tstr = NULL;
3202 
3203 		    if(wps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
3204 		      hcolors = spec_colors_from_varlist(wps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
3205 		    else
3206 		      hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
3207 		    for(thc = hcolors; thc; thc = thc->next){
3208 			resObj = Tcl_NewListObj(0,NULL);
3209 			Tcl_ListObjAppendElement(interp, resObj,
3210 						 Tcl_NewStringObj(thc->spec, -1));
3211 			hex_colorstr(hexcolor, thc->fg);
3212 			Tcl_ListObjAppendElement(interp, resObj,
3213 						 Tcl_NewStringObj(hexcolor, -1));
3214 			hex_colorstr(hexcolor, thc->bg);
3215 			Tcl_ListObjAppendElement(interp, resObj,
3216 						 Tcl_NewStringObj(hexcolor, -1));
3217 			Tcl_ListObjAppendElement(interp, resObj,
3218 						 Tcl_NewStringObj(thc->val
3219 								  ? tstr = pattern_to_string(thc->val)
3220 								  : "", -1));
3221 			if(tstr) fs_give((void **)&tstr);
3222 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3223 						 resObj);
3224 		    }
3225 		    fs_give((void **)&hcolors);
3226 		    return(TCL_OK);
3227 		}
3228 		else {
3229 		    char *colorp;
3230 
3231 		    snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
3232 
3233 		    for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
3234 			vtmp->name && strucmp(vtmp->name, tvname);
3235 			vtmp++)
3236 		      ;
3237 
3238 		    if(!vtmp->name) return(TCL_ERROR);
3239 		    if(vtmp->is_list) return(TCL_ERROR);
3240 
3241 		    colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
3242 			      ? vtmp->changed_val.p
3243 			      : (vtmp->current_val.p) ? vtmp->current_val.p
3244 				   :  vtmp->global_val.p;
3245 
3246 		    if(colorp){
3247 			hex_colorstr(hexcolor, colorp);
3248 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3249 						 Tcl_NewStringObj(hexcolor, -1));
3250 		    }
3251 		    else
3252 		      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3253 					       Tcl_NewStringObj("", -1));
3254 
3255 		    snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
3256 		    vtmp++;
3257 		    if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
3258 		      for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
3259 			  vtmp->name && strucmp(vtmp->name, tvname);
3260 			  vtmp++)
3261 			;
3262 
3263 		    if(!vtmp->name) return(TCL_ERROR);
3264 		    if(vtmp->is_list) return(TCL_ERROR);
3265 
3266 		    colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
3267 			      ? vtmp->changed_val.p
3268 			      : (vtmp->current_val.p) ? vtmp->current_val.p
3269 				   :  vtmp->global_val.p;
3270 
3271 		    if(colorp){
3272 			hex_colorstr(hexcolor, colorp);
3273 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3274 						 Tcl_NewStringObj(hexcolor, -1));
3275 		    }
3276 		    else
3277 		      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3278 					       Tcl_NewStringObj("", -1));
3279 		}
3280 		return(TCL_OK);
3281 	    }
3282 	    else if(!strcmp(s1, "cldel")){
3283 		int cl, i, j, n;
3284 		struct variable *vtmp;
3285 		char **newl;
3286 
3287 		if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3288 		  return(TCL_ERROR);
3289 		vtmp = &wps_global->vars[V_FOLDER_SPEC];
3290 		for(i = 0; i < cl && (vtmp->is_changed_val
3291 				      ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3292 				      : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
3293 		if(!(i == cl && (vtmp->is_changed_val
3294 				 ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3295 				 : (vtmp->current_val.l && vtmp->current_val.l[i])))){
3296 		    vtmp = &wps_global->vars[V_NEWS_SPEC];
3297 		    for(j = 0; i + j < cl && (vtmp->is_changed_val
3298 					      ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3299 					      : (vtmp->current_val.l && vtmp->current_val.l[j]));
3300 			j++);
3301 		    if(!(vtmp->is_changed_val
3302 			 ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3303 			 : (vtmp->current_val.l && vtmp->current_val.l[j])))
3304 		      return(TCL_ERROR);
3305 		    i = j;
3306 		}
3307 		for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
3308 		      : (vtmp->current_val.l && vtmp->current_val.l[n]); n++);
3309 		newl = (char **)fs_get(n*(sizeof(char *)));
3310 		for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
3311 		      : (vtmp->current_val.l && vtmp->current_val.l[n]); n++){
3312 		    if(n < i)
3313 		      newl[n] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
3314 				       : vtmp->current_val.l[n]);
3315 		    else if(n > i)
3316 		      newl[n-1] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
3317 					 : vtmp->current_val.l[n]);
3318 		}
3319 		newl[n-1] = NULL;
3320 		vtmp->is_changed_val = 1;
3321 		for(n = 0; vtmp->changed_val.l && vtmp->changed_val.l[n]; n++)
3322 		  fs_give((void **) &vtmp->changed_val.l[n]);
3323 		if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3324 		vtmp->changed_val.l = newl;
3325 
3326 		return(TCL_OK);
3327 	    }
3328 	    else if(!strcmp(s1, "columns")){
3329 		int   n;
3330 		char *p;
3331 
3332 		if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR
3333 		   && n >= MIN_SCREEN_COLS
3334 		   && n < (MAX_SCREEN_COLS - 1)
3335 		   && wps_global->ttyo->screen_cols != n){
3336 		    clear_index_cache(sp_inbox_stream(), 0);
3337 		    wps_global->ttyo->screen_cols = n;
3338 		    set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main);
3339 		    Tcl_SetResult(interp, p, TCL_VOLATILE);
3340 		}
3341 		else
3342 		  Tcl_SetResult(interp, int2string(wps_global->ttyo->screen_cols), TCL_VOLATILE);
3343 
3344 		return(TCL_OK);
3345 	    }
3346 	    else if(!strcmp(s1, "reset")){
3347 		char *p;
3348 
3349 		if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
3350 		    if(!strcmp(p,"pinerc")){
3351 			struct variable *var;
3352 			PINERC_S	*prc;
3353 
3354 			/* new pinerc structure, copy location pointers */
3355 			prc = new_pinerc_s(wps_global->prc->name);
3356 			prc->type = wps_global->prc->type;
3357 			prc->rd = wps_global->prc->rd;
3358 			prc->outstanding_pinerc_changes = 1;
3359 
3360 			/* tie off original pinerc struct and free it */
3361 			wps_global->prc->rd = NULL;
3362 			wps_global->prc->outstanding_pinerc_changes = 0;
3363 			free_pinerc_s(&wps_global->prc);
3364 
3365 			/* set global->prc to new struct with no pinerc_lines
3366 			 * and fool write_pinerc into not writing changed vars
3367 			 */
3368 			wps_global->prc = prc;
3369 
3370 			/*
3371 			 * write at least one var into nearly empty pinerc
3372 			 * and clear user's var settings. clear global cause
3373 			 * they'll get reset in peInitVars
3374 			 */
3375 			for(var = wps_global->vars; var->name != NULL; var++){
3376 			    var->been_written = ((var - wps_global->vars) != V_LAST_VERS_USED);
3377 			    if(var->is_list){
3378 				free_list_array(&var->main_user_val.l);
3379 				free_list_array(&var->global_val.l);
3380 			    }
3381 			    else{
3382 				fs_give((void **)&var->main_user_val.p);
3383 				fs_give((void **)&var->global_val.p);
3384 			    }
3385 			}
3386 
3387 			write_pinerc(wps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN);
3388 
3389 			peInitVars(wps_global);
3390 			return(TCL_OK);
3391 		    }
3392 		}
3393 	    }
3394 	}
3395 	else if(objc == 4){
3396 	    if(!strcmp(s1, "varset")){
3397 		char *varname = Tcl_GetStringFromObj(objv[2], NULL);
3398 		struct variable *vtmp;
3399 		char  **tstrlist = NULL, *line, *tline;
3400 		Tcl_Obj **objVal;
3401 		int i, strlistpos, numlistvals;
3402 
3403 		if(varname == NULL) return(TCL_ERROR);
3404 		for(vtmp = wps_global->vars;
3405 		    vtmp->name && strucmp(vtmp->name, varname);
3406 		    vtmp++)
3407 		  ;
3408 		if(!vtmp->name){
3409 		    Tcl_SetResult(interp, err, TCL_VOLATILE);
3410 		    return(TCL_ERROR);
3411 		}
3412 		if(Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
3413 					  &objVal) != TCL_OK)
3414 		  return(TCL_ERROR);
3415 		vtmp->is_changed_val = 1;
3416 		if(vtmp->is_list){
3417 		    if(vtmp->changed_val.l){
3418 			for(i = 0; vtmp->changed_val.l[i]; i++)
3419 			  fs_give((void **)&vtmp->changed_val.l[i]);
3420 			fs_give((void **)&vtmp->changed_val.l);
3421 		    }
3422 		    if(numlistvals)
3423 		      tstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
3424 		    for(i = 0, strlistpos = 0; i < numlistvals; i++){
3425 			if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
3426 			    tline = cpystr(line);
3427 			    removing_leading_and_trailing_white_space(tline);
3428 			    if(*tline)
3429 			      tstrlist[strlistpos++] = cpystr(tline);
3430 			    fs_give((void **) &tline);
3431 			}
3432 		    }
3433 		    if(tstrlist)
3434 		      tstrlist[strlistpos] = NULL;
3435 		    vtmp->changed_val.l = tstrlist;
3436 		}
3437 		else {
3438 		    if(vtmp->changed_val.p)
3439 		      fs_give((void **)&vtmp->changed_val.p);
3440 		    if(numlistvals){
3441 			if((line = Tcl_GetStringFromObj(objVal[0], 0)) != NULL){
3442 			    tline = cpystr(line);
3443 			    if(strucmp(vtmp->name, "reply-indent-string"))
3444 			      removing_leading_and_trailing_white_space(tline);
3445 			    if(!strcmp(tline, "\"\"")){
3446 				tline[0] = '\0';
3447 			    }
3448 			    else if(tline[0] == '\0'){
3449 				fs_give((void **)&tline);
3450 			    }
3451 			    if(tline){
3452 				vtmp->changed_val.p = cpystr(tline);
3453 				fs_give((void **)&tline);
3454 			    }
3455 			}
3456 			else
3457 			  vtmp->changed_val.p = cpystr("");
3458 		    }
3459 		}
3460 		return(TCL_OK);
3461 	    }
3462 	    else if(!strcmp(s1, "feature")){
3463 		char      *featurename;
3464 		int	       i, set, wasset = 0;
3465 		FEATURE_S *feature;
3466 
3467 		    /*
3468 		     * CMD: feature
3469 		     *
3470 		     * ARGS: featurename -
3471 		     *	     value - new value to assign flag
3472 		     *
3473 		     * Returns: 1 if named feature set, 0 otherwise
3474 		     *
3475 		     */
3476 		if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
3477 		   && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
3478 		  for(i = 0; (feature = feature_list(i)); i++)
3479 		    if(!strucmp(featurename, feature->name)){
3480 			wps_global->vars[V_FEATURE_LIST].is_changed_val = 1;
3481 			wasset = F_CH_ON(feature->id);
3482 			F_CH_SET(feature->id, set);
3483 			break;
3484 		    }
3485 
3486 		Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
3487 		return(TCL_OK);
3488 	    }
3489 	    else if(!strcmp(s1, "clshuff")){
3490 		char *dir, *tstr, **newl;
3491 		int cl, up = 0, fvarn, nvarn, icnt, i;
3492 		struct variable *fvar, *nvar, *vtmp;
3493 
3494 		if(!(dir = Tcl_GetStringFromObj(objv[2], NULL)))
3495 		  return TCL_ERROR;
3496 		if(Tcl_GetIntFromObj(interp, objv[3], &cl) == TCL_ERROR)
3497 		  return(TCL_ERROR);
3498 		if(!strcmp(dir, "up"))
3499 		  up = 1;
3500 		else if(!strcmp(dir, "down"))
3501 		  up = 0;
3502 		else
3503 		  return(TCL_ERROR);
3504 		fvar = &wps_global->vars[V_FOLDER_SPEC];
3505 		nvar = &wps_global->vars[V_NEWS_SPEC];
3506 		for(fvarn = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[fvarn])
3507 		      : (fvar->current_val.l && fvar->current_val.l[fvarn]); fvarn++);
3508 		for(nvarn = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[nvarn])
3509 		      : (nvar->current_val.l && nvar->current_val.l[nvarn]); nvarn++);
3510 		if(cl < fvarn){
3511 		    vtmp = fvar;
3512 		    icnt = cl;
3513 		}
3514 		else if(cl >= fvarn && cl < nvarn + fvarn){
3515 		    vtmp = nvar;
3516 		    icnt = cl - fvarn;
3517 		}
3518 		else
3519 		  return(TCL_ERROR);
3520 		if(vtmp == nvar && icnt == 0 && up){
3521 		    newl = (char **)fs_get((fvarn + 2)*sizeof(char *));
3522 		    for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i])
3523 			  : (fvar->current_val.l && fvar->current_val.l[i]); i++)
3524 		      newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3525 				       : fvar->current_val.l[i]);
3526 		    newl[i++] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[0]
3527 				       : nvar->current_val.l[0]);
3528 		    newl[i] = NULL;
3529 		    fvar->is_changed_val = 1;
3530 		    for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
3531 		      fs_give((void **)&fvar->changed_val.l[i]);
3532 		    if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
3533 		    fvar->changed_val.l = newl;
3534 		    newl = (char **)fs_get(nvarn*sizeof(char *));
3535 		    for(i = 1; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
3536 			  : (nvar->current_val.l && nvar->current_val.l[i]); i++)
3537 		      newl[i-1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
3538 					 : nvar->current_val.l[i]);
3539 		    newl[i-1] = NULL;
3540 		    nvar->is_changed_val = 1;
3541 		    for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
3542 		      fs_give((void **)&nvar->changed_val.l[i]);
3543 		    if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
3544 		    nvar->changed_val.l = newl;
3545 		    vtmp = fvar;
3546 		    icnt = fvarn;
3547 		}
3548 		else if(vtmp == fvar && icnt == fvarn - 1 && !up){
3549 		    newl = (char **)fs_get(fvarn*sizeof(char *));
3550 		    for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i+1])
3551 			  : (fvar->current_val.l && fvar->current_val.l[i+1]); i++)
3552 		      newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3553 				       : fvar->current_val.l[i]);
3554 		    newl[i] = NULL;
3555 		    tstr = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3556 				  : fvar->current_val.l[i]);
3557 		    fvar->is_changed_val = 1;
3558 		    for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
3559 		      fs_give((void **)&fvar->changed_val.l[i]);
3560 		    if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
3561 		    fvar->changed_val.l = newl;
3562 		    newl = (char **)fs_get((nvarn+2)*sizeof(char *));
3563 		    newl[0] = tstr;
3564 		    for(i = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
3565 			  : (nvar->current_val.l && nvar->current_val.l[i]); i++)
3566 		      newl[i+1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
3567 					 : nvar->current_val.l[i]);
3568 		    newl[i+1] = NULL;
3569 		    nvar->is_changed_val = 1;
3570 		    for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
3571 		      fs_give((void **)&nvar->changed_val.l[i]);
3572 		    if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
3573 		    nvar->changed_val.l = newl;
3574 		    vtmp = nvar;
3575 		    icnt = 0;
3576 		}
3577 		else {
3578 		    newl = (char **)fs_get(((vtmp == fvar ? fvarn : nvarn) + 1)*sizeof(char *));
3579 		    for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3580 			  : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
3581 		      newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
3582 				    : vtmp->current_val.l[i]);
3583 		    newl[i] = NULL;
3584 		    vtmp->is_changed_val = 1;
3585 		    for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
3586 		      fs_give((void **)&vtmp->changed_val.l[i]);
3587 		    if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3588 		    vtmp->changed_val.l = newl;
3589 		}
3590 		if(up){
3591 		    tstr = vtmp->changed_val.l[icnt-1];
3592 		    vtmp->changed_val.l[icnt-1] = vtmp->changed_val.l[icnt];
3593 		    vtmp->changed_val.l[icnt] = tstr;
3594 		}
3595 		else {
3596 		    tstr = vtmp->changed_val.l[icnt+1];
3597 		    vtmp->changed_val.l[icnt+1] = vtmp->changed_val.l[icnt];
3598 		    vtmp->changed_val.l[icnt] = tstr;
3599 		}
3600 		return(TCL_OK);
3601 	    }
3602 	}
3603 	else if(objc == 7){
3604 	    if(!strcmp(s1, "cledit") || !strcmp(s1, "cladd")){
3605 		int add = 0, cl, quotes_needed = 0, i, j, newn;
3606 		char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4];
3607 		char **newl;
3608 		struct variable *vtmp;
3609 
3610 		if(!strcmp(s1, "cladd")) add = 1;
3611 
3612 		if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3613 		  return(TCL_ERROR);
3614 		if(!(nick = Tcl_GetStringFromObj(objv[3], NULL)))
3615 		  return TCL_ERROR;
3616 		if(!(server = Tcl_GetStringFromObj(objv[4], NULL)))
3617 		  return TCL_ERROR;
3618 		if(!(path = Tcl_GetStringFromObj(objv[5], NULL)))
3619 		  return TCL_ERROR;
3620 		if(!(view = Tcl_GetStringFromObj(objv[6], NULL)))
3621 		  return TCL_ERROR;
3622 		removing_leading_and_trailing_white_space(nick);
3623 		removing_leading_and_trailing_white_space(server);
3624 		removing_leading_and_trailing_white_space(path);
3625 		removing_leading_and_trailing_white_space(view);
3626 		if(strchr(nick, ' '))
3627 		  quotes_needed = 1;
3628 		if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
3629 		   MAILTMPLEN * 4 - 20) { /* for good measure */
3630 		    Tcl_SetResult(interp, "info too long", TCL_VOLATILE);
3631 		    return TCL_ERROR;
3632 		}
3633 		if(3 + strlen(nick) + strlen(server) + strlen(path) +
3634 		   strlen(view) > MAILTMPLEN + 4){
3635 		    Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE);
3636 		    return(TCL_OK);
3637 		}
3638 		snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
3639 			"\"" : "", nick, quotes_needed ? "\"" : "",
3640 			strlen(nick) ? " " : "",
3641 			server, path, view);
3642 		if(add) {
3643 		    vtmp = &wps_global->vars[V_NEWS_SPEC];
3644 		    if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[0])
3645 		       : (vtmp->current_val.l && vtmp->current_val.l[0])))
3646 		      vtmp = &wps_global->vars[V_FOLDER_SPEC];
3647 		    for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3648 			  : (vtmp->current_val.l && vtmp->current_val.l[i]); i++);
3649 		    newn = i + 1;
3650 		    newl = (char **)fs_get((newn + 1)*sizeof(char *));
3651 		    for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3652 			  : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
3653 		      newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
3654 				       : vtmp->current_val.l[i]);
3655 		    newl[i++] = cpystr(context_buf);
3656 		    newl[i] = NULL;
3657 		}
3658 		else {
3659 		    vtmp = &wps_global->vars[V_FOLDER_SPEC];
3660 		    for(i = 0; i < cl && (vtmp->is_changed_val
3661 					  ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3662 					  : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
3663 		    if(!(i == cl && (vtmp->is_changed_val
3664 				     ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3665 				     : (vtmp->current_val.l && vtmp->current_val.l[i])))){
3666 			vtmp = &wps_global->vars[V_NEWS_SPEC];
3667 			for(j = 0; i + j < cl && (vtmp->is_changed_val
3668 						  ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3669 						  : (vtmp->current_val.l && vtmp->current_val.l[j]));
3670 			    j++);
3671 			if(!(vtmp->is_changed_val
3672 			     ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3673 			     : (vtmp->current_val.l && vtmp->current_val.l[j])))
3674 			  return(TCL_ERROR);
3675 			i = j;
3676 		    }
3677 		    for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3678 			  : (vtmp->current_val.l && vtmp->current_val.l[j]); j++);
3679 		    newl = (char **)fs_get(j * sizeof(char *));
3680 		    for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3681 			  : (vtmp->current_val.l && vtmp->current_val.l[j]); j++){
3682 			if(j == i)
3683 			  newl[j] = cpystr(context_buf);
3684 			else
3685 			  newl[j] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[j]
3686 					   : vtmp->current_val.l[j]);
3687 		    }
3688 		    newl[j] = NULL;
3689 		}
3690 		vtmp->is_changed_val = 1;
3691 		for(j = 0; vtmp->changed_val.l && vtmp->changed_val.l[j]; j++)
3692 		  fs_give((void **)&vtmp->changed_val.l[j]);
3693 		if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3694 		vtmp->changed_val.l = newl;
3695 		return TCL_OK;
3696 	    }
3697 	}
3698 	else
3699 	  err = "PEInfo: Too many arguments";
3700     }
3701     Tcl_SetResult(interp, err, TCL_STATIC);
3702     return(TCL_ERROR);
3703 }
3704 
3705 
3706 int
peWriteSig(Tcl_Interp * interp,char * file,Tcl_Obj ** objv)3707 peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv)
3708 {
3709     int        try_cache, e, i, n, nSig;
3710     char       datebuf[200], *sig, *line;
3711     FILE      *fp;
3712     REMDATA_S *rd;
3713     Tcl_Obj  **objSig;
3714 
3715     if(!(file && IS_REMOTE(file))){
3716 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
3717 		file ? file : "<null>");
3718 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3719 	return(TCL_ERROR);
3720     }
3721 
3722     /*
3723      * We could parse the name here to find what type it is. So far we
3724      * only have type RemImap.
3725      */
3726     rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE,
3727 			  NULL, "Error: ", "Can't fetch remote signature.");
3728     if(!rd){
3729 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file);
3730 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3731 	return(TCL_ERROR);
3732     }
3733 
3734     try_cache = rd_read_metadata(rd);
3735 
3736     if(rd->access == MaybeRorW){
3737 	if(rd->read_status == 'R')
3738 	  rd->access = ReadOnly;
3739 	else
3740 	  rd->access = ReadWrite;
3741     }
3742 
3743     if(rd->access != NoExists){
3744 
3745 	rd_check_remvalid(rd, 1L);
3746 
3747 	/*
3748 	 * If the cached info says it is readonly but
3749 	 * it looks like it's been fixed now, change it to readwrite.
3750 	 */
3751         if(rd->read_status == 'R'){
3752 	    /*
3753 	     * We go to this trouble since readonly sigfiles
3754 	     * are likely a mistake. They are usually supposed to be
3755 	     * readwrite so we open it and check if it's been fixed.
3756 	     */
3757 	    rd_check_readonly_access(rd);
3758 	    if(rd->read_status == 'W'){
3759 		rd->access = ReadWrite;
3760 		rd->flags |= REM_OUTOFDATE;
3761 	    }
3762 	    else{
3763 		rd_close_remdata(&rd);
3764 		snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file);
3765 		Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3766 		return(TCL_ERROR);
3767 	    }
3768 	}
3769 
3770 	if(rd->flags & REM_OUTOFDATE){
3771 	    if(rd_update_local(rd) != 0){
3772 
3773 		dprint((1, "pinerc_remote_open: rd_update_local failed"));
3774 		/*
3775 		 * Don't give up altogether. We still may be
3776 		 * able to use a cached copy.
3777 		 */
3778 	    }
3779 	    else{
3780 		dprint((7, "%s: copied remote to local (%ld)",
3781 			rd->rn, (long)rd->last_use));
3782 	    }
3783 	}
3784 
3785 	if(rd->access == ReadWrite)
3786 	  rd->flags |= DO_REMTRIM;
3787     }
3788 
3789     /* If we couldn't get to remote folder, try using the cached copy */
3790     if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
3791 	rd_close_remdata(&rd);
3792 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file);
3793 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3794 	return(TCL_ERROR);
3795     }
3796 
3797     unlink(rd->lf);
3798 
3799     sig = NULL;
3800     wtmp_20k_buf[0] = '\0';
3801     if(objv){
3802 	Tcl_ListObjGetElements(interp, objv[0], &nSig, &objSig);
3803 	for(i = 0; i < nSig && i < SIG_MAX_LINES; i++){
3804 	    if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
3805 	      snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n",
3806 		      SIG_MAX_COLS, line);
3807 	}
3808     }
3809     else if(peTSig){
3810 	for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) {
3811 	    snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n",
3812 		    SIG_MAX_COLS, peTSig[i]);
3813 	}
3814 	for(i = 0; peTSig[i]; i++)
3815 	  fs_give((void **)&peTSig[i]);
3816 	fs_give((void **)&peTSig);
3817     }
3818     else
3819       return(TCL_ERROR);
3820 
3821     sig = cpystr(wtmp_20k_buf);
3822 
3823     if((fp = fopen(rd->lf, "w")) != NULL)
3824       n = fwrite(sig, strlen(sig), 1, fp);
3825 
3826     fs_give((void **) &sig);
3827 
3828     if(fp){
3829 	if(n != 1){
3830 	    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s",
3831 		    rd->lf, error_description(errno));
3832 	    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3833 	    rd_close_remdata(&rd);
3834 	}
3835 
3836 	fclose(fp);
3837 	if(n != 1)
3838 	  return(TCL_ERROR);
3839     }
3840     else {
3841 	rd_close_remdata(&rd);
3842 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s",
3843 		rd->lf, error_description(errno));
3844 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3845 	return(TCL_ERROR);
3846     }
3847 
3848     datebuf[0] = '\0';
3849 
3850     if(!rd->t.i.stream){
3851 	long retflags = 0;
3852 
3853 	rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags);
3854     }
3855 
3856     if((e = rd_update_remote(rd, datebuf)) != 0){
3857 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s",
3858 		rd->lf, error_description(errno));
3859 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3860 	rd_close_remdata(&rd);
3861 	return(TCL_ERROR);
3862     }
3863 
3864     rd_update_metadata(rd, datebuf);
3865     rd->read_status = 'W';
3866     rd_close_remdata(&rd);
3867     return(TCL_OK);
3868 }
3869 
3870 
3871 
sort_key_rules(index)3872 NAMEVAL_S   *sort_key_rules(index)
3873      int index;
3874 {
3875     static NAMEVAL_S is_rules[] = {
3876         {"Arrival",		0},
3877         {"Date",		0},
3878         {"Subject",		0},
3879         {"Cc",			0},
3880         {"From",		0},
3881         {"To",			0},
3882         {"size",		0},
3883 	{"OrderedSubj",		0},
3884 	{"tHread",		0},
3885         {"Arrival/Reverse",	0},
3886         {"Date/Reverse",	0},
3887         {"Subject/Reverse",	0},
3888         {"Cc/Reverse",		0},
3889         {"From/Reverse",	0},
3890         {"To/Reverse",		0},
3891         {"size/Reverse",	0},
3892 	{"tHread/Reverse",	0},
3893 	{"OrderedSubj/Reverse",	0}
3894     };
3895 
3896     return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3897 	   ? &is_rules[index] : NULL);
3898 }
3899 
wp_indexheight_rules(index)3900 NAMEVAL_S   *wp_indexheight_rules(index)
3901      int index;
3902 {
3903     static NAMEVAL_S is_rules[] = {
3904 	{"normal font",   "24",   0},
3905 	{"smallest font", "20",   0},
3906 	{"small font",    "22",   0},
3907 	{"large font",    "28",   0},
3908 	{"largest font",  "30",   0}
3909     };
3910 
3911     return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3912 	   ? &is_rules[index] : NULL);
3913 }
3914 
3915 
3916 /*
3917  * PEDebugCmd - turn on/off and set various debugging options
3918  */
3919 int
PEDebugCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3920 PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
3921 {
3922     char *s;
3923 
3924     if(!--objc){		/* only one arg? */
3925 	Tcl_WrongNumArgs(interp, 1, objv, "?args?");
3926     }
3927     else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
3928 	if(!strucmp(s, "level")){
3929 	    if(objc == 2){
3930 		int level;
3931 
3932 		if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3933 		  return(TCL_ERROR);
3934 
3935 		if(level > 0){
3936 		    if(level > 10)
3937 		      level = 10;
3938 
3939 		    debug = level;
3940 		    dprint((1, "Debug level %d", level));
3941 		}
3942 		else{
3943 		    dprint((1, "PEDebug ending"));
3944 		    debug = 0;
3945 		}
3946 	    }
3947 
3948 	    Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE);
3949 	    return(TCL_OK);
3950 	}
3951 	else if(!strucmp(s, "write")){
3952 	    if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){
3953 		/*
3954 		 * script debugging has a high priority since
3955 		 * statements can be added/removed on the fly
3956 		 * AND are NOT present by default
3957 		 */
3958 		dprint((SYSDBG_INFO, "SCRIPT: %s", s));
3959 	    }
3960 
3961 	    return(TCL_OK);
3962 	}
3963 	else if(!strucmp(s, "imap")){
3964 	    int level;
3965 
3966 	    if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3967 	      return(TCL_ERROR);
3968 
3969 	    if(level == 0){
3970 		if(wps_global){
3971 		    wps_global->debug_imap = 0;
3972 		    if(wps_global->mail_stream)
3973 		      mail_nodebug(wps_global->mail_stream);
3974 		}
3975 	    }
3976 	    else if(level > 0 && level < 5){
3977 		if(wps_global){
3978 		    wps_global->debug_imap = level;
3979 		    if(wps_global->mail_stream)
3980 		      mail_debug(wps_global->mail_stream);
3981 		}
3982 	    }
3983 
3984 	    return(TCL_OK);
3985 	}
3986 	else
3987 	  Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC);
3988     }
3989 
3990     return(TCL_ERROR);
3991 }
3992 
3993 
3994 /*
3995  * PESessionCmd - Export TCL Session-wide command set
3996  */
3997 int
PESessionCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3998 PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
3999 {
4000     char *op, *err = "Unknown PESession option";
4001     char *pe_user, *pe_host;
4002     int	  pe_alt, l;
4003 
4004     dprint((2, "PESessionCmd"));
4005 
4006     if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4007 	if(!strcmp(op, "open")){
4008 	    char *s, *pinerc, *pineconf = NULL;
4009 
4010 	    /*
4011 	     * CMD: open user remote-pinerc local-default-config
4012 	     *
4013 	     * Initiate a session
4014 	     *
4015 	     * Returns: error string on error, nothing otherwise
4016 	     */
4017 
4018 	    if(objc < 4 || objc > 5){
4019 		Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc");
4020 		return(TCL_ERROR);
4021 	    }
4022 
4023 	    if(!(s = Tcl_GetStringFromObj(objv[2], &l))){
4024 		Tcl_SetResult(interp, "Unknown User", TCL_STATIC);
4025 		return(TCL_ERROR);
4026 	    }
4027 	    else{
4028 		int rv;
4029 
4030 		pe_user = cpystr(s);
4031 
4032 #if	defined(HAVE_SETENV)
4033 		rv = setenv("WPUSER", pe_user, 1);
4034 #elif	defined(HAVE_PUTENV)
4035 		{
4036 		    static char putenvbuf[PUTENV_MAX];
4037 
4038 		    if(l + 8 < PUTENV_MAX){
4039 			if(putenvbuf[0])	/* only called once, but you never know */
4040 			  snprintf(putenvbuf + 7, PUTENV_MAX - 7, "%s", pe_user);
4041 			else
4042 			  snprintf(putenvbuf, PUTENV_MAX, "WPUSER=%s", pe_user);
4043 
4044 			rv = putenv(putenvbuf);
4045 		    }
4046 		    else
4047 		      rv = 1;
4048 		}
4049 #endif
4050 
4051 		if(rv){
4052 		    fs_give((void **) &pe_user);
4053 		    Tcl_SetResult(interp, (errno == ENOMEM)
4054 					    ? "Insufficient Environment Space"
4055 					    : "Cannot set WPUSER in environment", TCL_STATIC);
4056 		    return(TCL_ERROR);
4057 		}
4058 	    }
4059 
4060 	    if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4061 		NETMBX  mb;
4062 
4063 		if(mail_valid_net_parse(pinerc, &mb)){
4064 		    pe_host = cpystr(mb.host);
4065 		    pe_alt = (mb.sslflag || mb.tlsflag);
4066 		}
4067 		else {
4068 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc);
4069 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4070 		    return(TCL_ERROR);
4071 		}
4072 	    }
4073 	    else {
4074 		Tcl_SetResult(interp, "Unknown config location", TCL_STATIC);
4075 		return(TCL_ERROR);
4076 	    }
4077 
4078 	    if(objc == 5 && !(pineconf = Tcl_GetStringFromObj(objv[4], NULL))){
4079 		Tcl_SetResult(interp, "Can't determine global config", TCL_STATIC);
4080 		return(TCL_ERROR);
4081 	    }
4082 
4083 	    dprint((SYSDBG_INFO, "session (%s) %s - %s",
4084 		    pe_user, pinerc, pineconf ? pineconf : "<none>"));
4085 
4086 	    /* credential cache MUST already be seeded */
4087 
4088 	    /* destroy old user context */
4089 	    if(wps_global){
4090 		/* destroy open stream */
4091 		peDestroyStream(wps_global);
4092 
4093 		/* destroy old user context */
4094 		peDestroyUserContext(&wps_global);
4095 	    }
4096 
4097 	    /* Establish a user context */
4098 	    if((s = peCreateUserContext(interp, pe_user, pinerc, pineconf)) != NULL){
4099 		Tcl_SetResult(interp, s, TCL_VOLATILE);
4100 		return(TCL_ERROR);
4101 	    }
4102 
4103 	    fs_give((void **) &pe_user);
4104 	    fs_give((void **) &pe_host);
4105 
4106 	    return(TCL_OK);
4107 	}
4108 	else if(!strcmp(op, "close")){
4109 	    if(wps_global){
4110 		/* destroy any open stream */
4111 		peDestroyStream(wps_global);
4112 
4113 		/* destroy user context */
4114 		peDestroyUserContext(&wps_global);
4115 	    }
4116 
4117 	    Tcl_SetResult(interp, "BYE", TCL_STATIC);
4118 	    return(TCL_OK);
4119 	}
4120 	else if(!strcmp(op, "creds")){
4121 	    char *folder;
4122 	    int	  colid;
4123 
4124 	    if(objc < 4){
4125 		err = "creds: insufficient args";
4126 	    }
4127 	    else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
4128 		    && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
4129 		int	   i;
4130 		CONTEXT_S *cp;
4131 
4132 		/*
4133 		 * CMD: creds <collection-index> <folder> [user passwd]
4134 		 *
4135 		 * Test for valid credentials to access given folder
4136 		 *
4137 		 * Returns: 1 if so, 0 otherwise
4138 		 */
4139 
4140 		for(i = 0, cp = wps_global ? wps_global->context_list : NULL;
4141 		    i < 1 || cp != NULL ;
4142 		    i++, cp = cp->next)
4143 		  if(i == colid){
4144 		      int	  rv = 0;
4145 		      char	  tmp[MAILTMPLEN], *p;
4146 
4147 		      if(cp){
4148 			  if(folder[0] == '\0'){
4149 			      if(cp->use & CNTXT_INCMNG)
4150 				rv = 1;
4151 			      else
4152 				folder = "fake-fake";
4153 			  }
4154 			  else if((cp->use & CNTXT_INCMNG)
4155 				  && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4156 			    folder = p;
4157 		      }
4158 
4159 		      if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4160 			  NETMBX  mb;
4161 
4162 			  if(mail_valid_net_parse(tmp, &mb)){
4163 			      if(objc == 4){		/* check creds */
4164 				  if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
4165 				    strcpy(mb.user, p);
4166 
4167 				  if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag)))
4168 				    rv = 1;
4169 			      }
4170 			      else if(objc == 6){	/* set creds */
4171 				  char *user, *passwd;
4172 
4173 				  if((user = Tcl_GetStringFromObj(objv[4], NULL))
4174 				     && (passwd = Tcl_GetStringFromObj(objv[5], NULL))){
4175 				      if(*mb.user && strcmp(mb.user, user)){
4176 					  err = "creds: mismatched user names";
4177 					  break;
4178 				      }
4179 
4180 				      alpine_set_passwd(user, passwd, mb.host,
4181 							mb.sslflag
4182 							|| mb.tlsflag
4183 							|| (wps_global ? F_ON(F_PREFER_ALT_AUTH, wps_global) : 0));
4184 				      rv = 1;
4185 				  }
4186 				  else {
4187 				      err = "creds: unable to read credentials";
4188 				      break;
4189 				  }
4190 			      }
4191 			      else{
4192 				  err = "creds: invalid args";
4193 				  break;
4194 			      }
4195 			  }
4196 		      }
4197 
4198 		      (void) Tcl_ListObjAppendElement(interp,
4199 						      Tcl_GetObjResult(interp),
4200 						      Tcl_NewIntObj(rv));
4201 		      return(TCL_OK);
4202 		  }
4203 
4204 		err = "creds: Unrecognized collection ID";
4205 	    }
4206 	    else
4207 	      err = "creds: failure to acquire folder and collection ID";
4208 	}
4209 	else if(!strcmp(op, "nocred")){
4210 	    char *folder;
4211 	    int	  colid;
4212 
4213 	    if(!wps_global){
4214 		err = "No Session active";
4215 	    }
4216 	    else if(objc != 4){
4217 		err = "nocred: wrong number of args";
4218 	    }
4219 	    else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
4220 		    && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
4221 		int	   i;
4222 		CONTEXT_S *cp;
4223 
4224 		/*
4225 		 * CMD: nocred <collection-index> <folder>
4226 		 *
4227 		 * Test for valid credentials to access given folder
4228 		 *
4229 		 * Returns: 1 if so, 0 otherwise
4230 		 */
4231 
4232 		for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4233 		  if(i == colid){
4234 		      int	  rv = 0;
4235 		      char	  tmp[MAILTMPLEN], *p;
4236 
4237 		      if((cp->use & CNTXT_INCMNG)
4238 			 && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4239 			folder = p;
4240 
4241 		      if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4242 			  NETMBX  mb;
4243 
4244 			  if(mail_valid_net_parse(tmp, &mb)){
4245 			      if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
4246 				strcpy(mb.user, p);
4247 
4248 			      alpine_clear_passwd(mb.user, mb.host);
4249 			  }
4250 		      }
4251 
4252 		      (void) Tcl_ListObjAppendElement(interp,
4253 						      Tcl_GetObjResult(interp),
4254 						      Tcl_NewIntObj(rv));
4255 		      return(TCL_OK);
4256 		  }
4257 
4258 		err = "creds: Unrecognized collection ID";
4259 	    }
4260 	    else
4261 	      err = "creds: failure to acquire folder and collection ID";
4262 	}
4263 	else if(!strcmp(op, "acceptcert")){
4264 	    char       *certhost;
4265 	    STRLIST_S **p;
4266 
4267 	    if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){
4268 		for(p = &peCertHosts; *p; p = &(*p)->next)
4269 		  ;
4270 
4271 		*p = new_strlist(certhost);
4272 	    }
4273 
4274 	    err = "PESession: no server name";
4275 	}
4276 	else if(!strcmp(op, "random")){
4277 	    if(objc != 3){
4278 		err = "PESession: random <length>";
4279 	    } else {
4280 		char s[1025];
4281 		int  l;
4282 
4283 		if(Tcl_GetIntFromObj(interp,objv[2],&l) != TCL_ERROR){
4284 		    if(l <= 1024){
4285 			Tcl_SetResult(interp, peRandomString(s,l,PRS_MIXED_CASE), TCL_STATIC);
4286 			return(TCL_OK);
4287 		    }
4288 		    else
4289 		      err = "PESession: random length too long";
4290 		}
4291 		else
4292 		  err = "PESession: can't get random length";
4293 	    }
4294 	}
4295 	else if(!strcmp(op, "authdriver")){
4296 	    if(objc != 4){
4297 		err = "PESession: authdriver {add | remove} drivername";
4298 	    } else {
4299 		char *cmd, *driver;
4300 
4301 		if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
4302 		    if((driver = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4303 			if(!strcmp(cmd,"enable")){
4304 			    err = "PESession: authdriver enable disabled for the nonce";
4305 			}
4306 			else if(!strcmp(cmd,"disable")){
4307 			    if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){
4308 				snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver);
4309 				Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4310 				return(TCL_OK);
4311 			    }
4312 			    else{
4313 				snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver);
4314 				Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4315 				return(TCL_ERROR);
4316 			    }
4317 			}
4318 			else
4319 			  err = "PESession: unknown authdriver operation";
4320 		    }
4321 		    else
4322 		      err = "PESession: Can't read driver name";
4323 		}
4324 		else
4325 		  err = "PESesions: Can't read authdriver operation";
4326 	    }
4327 	}
4328 	else if(!strcmp(op, "abandon")){
4329 	    /*
4330 	     * CMD: abandon [timeout]
4331 	     *
4332 	     * Returns: nothing
4333 	     */
4334 
4335 	    if(objc != 3){
4336 		err = "PESession: abandon [timeout]";
4337 	    } else {
4338 		long t;
4339 
4340 		if(Tcl_GetLongFromObj(interp, objv[2], &t) == TCL_OK){
4341 		    /* ten second minimum and max of default */
4342 		    if(t > 0 && t <= PE_INPUT_TIMEOUT){
4343 			gPEAbandonTimeout = t;
4344 			return(TCL_OK);
4345 		    }
4346 		    else
4347 		      err = "unrecognized timeout";
4348 		}
4349 		else
4350 		  err = "Can't read timeout";
4351 	    }
4352 	}
4353 	else if(!strcmp(op, "noexpunge")){
4354 	    /*
4355 	     * CMD: noexpunge <state>
4356 	     *
4357 	     * Returns: nothing
4358 	     */
4359 
4360 	    if(objc != 3){
4361 		err = "PESession: noexpunge <state>";
4362 	    } else {
4363 		int onoff;
4364 
4365 		if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){
4366 		    if(onoff == 0 || onoff == 1){
4367 			wps_global->noexpunge_on_close = onoff;
4368 			return(TCL_OK);
4369 		    }
4370 
4371 		    err = "unrecognized on/off state";
4372 		}
4373 		else
4374 		  err = "Can't read on/off state";
4375 	    }
4376 	}
4377 	else if(!strcmp(op, "setpassphrase")){
4378 #ifdef SMIME
4379 	    char *passphrase;
4380 
4381 	    if(objc != 3){
4382 		err = "PESession: setpassphrase <state>";
4383 	    }
4384 	    else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){
4385 	      if(wps_global && wps_global->smime){
4386 	        strncpy((char *) wps_global->smime->passphrase, passphrase,
4387 			sizeof(wps_global->smime->passphrase));
4388 	        wps_global->smime->passphrase[sizeof(wps_global->smime->passphrase)-1] = '\0';
4389 	        wps_global->smime->entered_passphrase = 1;
4390 	        wps_global->smime->need_passphrase = 0;
4391 	        peED.uid = 0;
4392 		return(TCL_OK);
4393 	      }
4394 	    }
4395 #else
4396 	    err = "S/MIME not configured for this server";
4397 #endif /* SMIME */
4398 	}
4399 	else if(!strcmp(op, "expungecheck")) {
4400 	    /*
4401 	     * Return open folders and how many deleted messages they have
4402 	     *
4403 	     * return looks something like a list of these:
4404 	     * {folder-name number-deleted isinbox isincoming}
4405 	     */
4406 	    char *type;
4407 	    long delete_count;
4408 	    Tcl_Obj *resObj;
4409 
4410 	    if(objc != 3){
4411 		err = "PESession: expungecheck <type>";
4412 	    }
4413 	    else {
4414 		type = Tcl_GetStringFromObj(objv[2], NULL);
4415 		if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){
4416 
4417 		    if(wps_global->mail_stream != sp_inbox_stream()
4418 		       || strcmp(type, "current") == 0){
4419 			delete_count = count_flagged(wps_global->mail_stream, F_DEL);
4420 			resObj = Tcl_NewListObj(0, NULL);
4421 			Tcl_ListObjAppendElement(interp, resObj,
4422 						 Tcl_NewStringObj(pretty_fn(wps_global->cur_folder), -1));
4423 			Tcl_ListObjAppendElement(interp, resObj,
4424 						 Tcl_NewIntObj(delete_count));
4425 			Tcl_ListObjAppendElement(interp, resObj,
4426 						 Tcl_NewIntObj((wps_global->mail_stream
4427 								== sp_inbox_stream())
4428 							       ? 1 : 0));
4429 			Tcl_ListObjAppendElement(interp, resObj,
4430 						 Tcl_NewIntObj((wps_global->context_current->use & CNTXT_INCMNG)
4431 							       ? 1 : 0));
4432 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4433 						 resObj);
4434 		    }
4435 		    if(strcmp(type, "quit") == 0){
4436 			delete_count = count_flagged(sp_inbox_stream(), F_DEL);
4437 			resObj = Tcl_NewListObj(0, NULL);
4438 			Tcl_ListObjAppendElement(interp, resObj,
4439 						 Tcl_NewStringObj("INBOX", -1));
4440 			Tcl_ListObjAppendElement(interp, resObj,
4441 						 Tcl_NewIntObj(delete_count));
4442 			Tcl_ListObjAppendElement(interp, resObj,
4443 						 Tcl_NewIntObj(1));
4444 			Tcl_ListObjAppendElement(interp, resObj,
4445 						 Tcl_NewIntObj(1));
4446 			Tcl_ListObjAppendElement(interp,
4447 						 Tcl_GetObjResult(interp), resObj);
4448 		    }
4449 		    return(TCL_OK);
4450 		}
4451 		else
4452 		  err = "PESession: expungecheck unknown type";
4453 	    }
4454 	}
4455 	else if(!strcmp(op, "mailcheck")) {
4456 	    /*
4457 	     * CMD: mailcheck
4458 	     *
4459 	     * ARGS: reload -- "1" if we're reloading
4460 	     *       (vs. just checking newmail as a side effect
4461 	     *        of building a new page)
4462 	     *
4463 	     * Return list of folders with new or expunged messages
4464 	     *
4465 	     * return looks something like a list of these:
4466 	     * {new-count newest-uid announcement-msg}
4467 	     */
4468 	    int	   reload, force = UFU_NONE, rv;
4469 	    time_t now = time(0);
4470 
4471 	    if(objc <= 3){
4472 		if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
4473 		  reload = 0;
4474 
4475 		/* minimum 10 second between IMAP pings */
4476 		if(!time_of_last_input() || now - time_of_last_input() > 10){
4477 		    force = UFU_FORCE;
4478 		    if(!reload)
4479 		      peMarkInputTime();
4480 		}
4481 
4482 		peED.interp = interp;
4483 
4484 		/* check for new mail */
4485 		new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG);
4486 
4487 		if(!reload){			/* announced */
4488 		    zero_new_mail_count();
4489 		}
4490 
4491 		return(TCL_OK);
4492 	    }
4493 	    else
4494 	      err = "PESession: mailcheck <reload>";
4495 	}
4496     }
4497 
4498     Tcl_SetResult(interp, err, TCL_STATIC);
4499     return(TCL_ERROR);
4500 }
4501 
4502 
4503 
4504 /*
4505  * PEFolderChange - create context's directory chain
4506  *                  corresponding to list of given obj's
4507  *
4508  *    NOTE: caller should call reset_context_folders(cp) to
4509  *          clean up data structures this creates before returning
4510  */
4511 int
PEFolderChange(Tcl_Interp * interp,CONTEXT_S * cp,int objc,Tcl_Obj * CONST objv[])4512 PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[])
4513 {
4514   int i;
4515   FDIR_S *fp;
4516   char *folder;
4517 
4518   for(i = 0; i < objc; i++) {
4519     folder = Tcl_GetStringFromObj(objv[i], NULL);
4520     if(!folder) {
4521       Tcl_SetResult(interp, "PEFolderChange: Can't read folder", TCL_VOLATILE);
4522       reset_context_folders(cp);
4523       return(TCL_ERROR);
4524     }
4525 
4526     fp = next_folder_dir(cp, folder, 0, NULL); /* BUG: mail_stream? */
4527     fp->desc    = folder_lister_desc(cp, fp);
4528     fp->delim   = cp->dir->delim;
4529     fp->prev    = cp->dir;
4530     fp->status |= CNTXT_SUBDIR;
4531     cp->dir  = fp;
4532   }
4533 
4534   return(TCL_OK);
4535 }
4536 
4537 /*
4538  * PEMakeFolderString:
4539  */
4540 int
PEMakeFolderString(Tcl_Interp * interp,CONTEXT_S * cp,int objc,Tcl_Obj * CONST objv[],char ** ppath)4541 PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath)
4542 {
4543   int i;
4544   unsigned long size,len;
4545   char *portion,*path;
4546 
4547   size = 0;
4548   for(i = 0; i < objc; i++) {
4549     portion = Tcl_GetStringFromObj(objv[i], NULL);
4550     if(!portion) {
4551       Tcl_SetResult(interp, "PEMakeFolderString: Can't read folder",
4552 		    TCL_VOLATILE);
4553       return(TCL_ERROR);
4554     }
4555     if(i) size++;
4556     size += strlen(portion);
4557   }
4558 
4559   path = (char*) fs_get(size + 1);
4560   size = 0;
4561   for(i = 0; i < objc; i++) {
4562     portion = Tcl_GetStringFromObj(objv[i], NULL);
4563     len = strlen(portion);
4564     if(i) path[size++] = cp->dir->delim;
4565     memcpy(path + size, portion, len);
4566     size += len;
4567   }
4568   path[size] = '\0';
4569   if(ppath) *ppath = path; else fs_give((void**) &path);
4570   return(TCL_OK);
4571 }
4572 
4573 
4574 /*
4575  * PEFolderCmd - export various bits of folder information
4576  */
4577 int
PEFolderCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])4578 PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
4579 {
4580     char *op, errbuf[256], *err = "Unknown PEFolder request";
4581 
4582     dprint((2, "PEFolderCmd"));
4583 
4584     if(objc == 1){
4585 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
4586     }
4587     else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4588 	if(wps_global){
4589 	    if(objc == 2){
4590 		if(!strcmp(op, "current")){
4591 		    CONTEXT_S *cp;
4592 		    int	       i;
4593 
4594 		    /*
4595 		     * CMD: current
4596 		     *
4597 		     * Returns: string representing the name of the
4598 		     *		current mailbox
4599 		     */
4600 
4601 		    for(i = 0, cp = wps_global->context_list; cp && cp != wps_global->context_current; i++, cp = cp->next)
4602 		      ;
4603 
4604 		    if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK
4605 		       && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wps_global->cur_folder,-1)) == TCL_OK)
4606 		      return(TCL_OK);
4607 
4608 		    return(TCL_ERROR);
4609 		}
4610 		else if(!strcmp(op, "collections")){
4611 		    CONTEXT_S *cp;
4612 		    int	       i;
4613 
4614 		    /*
4615 		     * CMD: collections
4616 		     *
4617 		     * Returns: List of currently configured collections
4618 		     */
4619 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next){
4620 			Tcl_Obj *objv[3];
4621 
4622 			objv[0] = Tcl_NewIntObj(i);
4623 			objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1);
4624 			objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1);
4625 
4626 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4627 						 Tcl_NewListObj(3, objv));
4628 		    }
4629 
4630 		    return(TCL_OK);
4631 		}
4632 		else if(!strcmp(op, "defaultcollection")){
4633 		    int	       i;
4634 		    CONTEXT_S *cp;
4635 
4636 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4637 		      if(cp->use & CNTXT_SAVEDFLT){
4638 			  Tcl_SetResult(interp, int2string(i), TCL_STATIC);
4639 			  return(TCL_OK);
4640 		      }
4641 
4642 		    err = "PEFolder: isincoming: Invalid collection ID";
4643 		}
4644 		else if(!strcmp(op, "clextended")){
4645 		    CONTEXT_S *cp;
4646 		    int	       i;
4647 		    char       tpath[MAILTMPLEN], *p;
4648 
4649 		    /*
4650 		     * CMD: clextended
4651 		     *
4652 		     * Returns: Extended list of current collections
4653 		     *
4654 		     * Format:
4655 		     *    0) Collection Number
4656 		     *    1) Nickname
4657 		     *    2) Label
4658 		     *    3) Basically this is a flag to say if we can edit
4659 		     *    4) Server
4660 		     *    5) Path
4661 		     *    6) View
4662 		     */
4663 		    /*
4664 		     * had to get rid of this cause the args are changed
4665 		     *
4666 		     * if(strcmp("extended",
4667 		     *       Tcl_GetStringFromObj(objv[2], NULL))){
4668 		     * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE);
4669 		     * return(TCL_ERROR);
4670 		     * }
4671 		     */
4672 		    for(i = 0, cp = wps_global->context_list; cp ;
4673 			i++, cp = cp->next){
4674 			Tcl_Obj *objv[7];
4675 
4676 			objv[0] = Tcl_NewIntObj(i);
4677 			objv[1] = Tcl_NewStringObj(cp->nickname ?
4678 						   cp->nickname : "", -1);
4679 			objv[2] = Tcl_NewStringObj(cp->label ?
4680 						   cp->label : "", -1);
4681 			objv[3] = Tcl_NewIntObj(cp->var.v ? 1 : 0);
4682 			objv[4] = Tcl_NewStringObj(cp->server ?
4683 						   cp->server : "", -1);
4684 			tpath[0] = '\0';
4685 			if(cp->context){
4686 			    strncpy(tpath, (cp->context[0] == '{'
4687 					   && (p = strchr(cp->context, '}')))
4688 				   ? ++p
4689 				   : cp->context, sizeof(tpath));
4690 			    tpath[sizeof(tpath)-1] = '\0';
4691 			    if((p = strstr(tpath, "%s")) != NULL)
4692 			      *p = '\0';
4693 			}
4694 			objv[5] = Tcl_NewStringObj(tpath, -1);
4695 			objv[6] = Tcl_NewStringObj(cp->dir &&
4696 						   cp->dir->view.user ?
4697 						   cp->dir->view.user :
4698 						   "", -1);
4699 			Tcl_ListObjAppendElement(interp,
4700 						 Tcl_GetObjResult(interp),
4701 						 Tcl_NewListObj(7, objv));
4702 		    }
4703 
4704 		    return(TCL_OK);
4705 		}
4706 	    }
4707 	    else if(objc == 3 && !strcmp(op, "delimiter")){
4708 		int	       colid, i;
4709 		char       delim[2] = {'\0', '\0'};
4710 		CONTEXT_S *cp;
4711 
4712 		if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4713 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4714 		      if(i == colid){
4715 			  if(cp->dir && cp->dir->delim)
4716 			    delim[0] = cp->dir->delim;
4717 
4718 			  break;
4719 		      }
4720 
4721 		    Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE);
4722 		    return(TCL_OK);
4723 		}
4724 		else
4725 		  err = "PEFolder: delimiter: Can't read collection ID";
4726 	    }
4727 	    else if(objc == 3 && !strcmp(op, "isincoming")){
4728 		int	   colid, i;
4729 		CONTEXT_S *cp;
4730 
4731 		if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4732 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4733 		      if(i == colid){
4734 			  Tcl_SetResult(interp, int2string(((cp->use & CNTXT_INCMNG) != 0)), TCL_STATIC);
4735 			  return(TCL_OK);
4736 		      }
4737 
4738 		    err = "PEFolder: isincoming: Invalid collection ID";
4739 		}
4740 		else
4741 		  err = "PEFolder: isincoming: Can't read collection ID";
4742 	    }
4743 	    else if(objc == 4 && !strcmp(op, "unread")){
4744 		char       *folder, tmp[MAILTMPLEN];
4745 		MAILSTREAM *mstream;
4746 		CONTEXT_S  *cp;
4747 		long	    colid, i, count = 0, flags = (F_UNSEEN | F_UNDEL);
4748 		int	    our_stream = 0;
4749 		/*
4750 		 * CMD: unread
4751 		 *
4752 		 * Returns: number of unread messages in given
4753 		 *          folder
4754 		 */
4755 		if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4756 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4757 		      if(i == colid)
4758 			break;
4759 
4760 		    if(cp){
4761 			if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4762 			    /* short circuit INBOX */
4763 			    if(colid == 0 && !strucmp(folder, "inbox")){
4764 				count = count_flagged(sp_inbox_stream(), flags);
4765 			    }
4766 			    else{
4767 				/*
4768 				 * BUG: some sort of caching to prevent open() fore each call?
4769 				 * does stream cache offset this?
4770 				 */
4771 				if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4772 				     && (mstream = same_stream_and_mailbox(tmp, wps_global->mail_stream)))){
4773 				    long retflags = 0;
4774 
4775 				    wps_global->noshow_error = 1;
4776 				    our_stream = 1;
4777 				    mstream = context_open(cp, NULL, folder,
4778 							   SP_USEPOOL | SP_TEMPUSE| OP_READONLY | OP_SHORTCACHE,
4779 							   &retflags);
4780 				    wps_global->noshow_error = 0;
4781 				}
4782 
4783 				count = count_flagged(mstream, flags);
4784 
4785 				if(our_stream)
4786 				  pine_mail_close(mstream);
4787 			    }
4788 
4789 			    Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4790 			    return(TCL_OK);
4791 			}
4792 		    }
4793 		    else
4794 		      err = "PEFolder: unread: Invalid collection ID";
4795 		}
4796 		else
4797 		  err = "PEFolder: unread: Can't read collection ID";
4798 	    }
4799 	    else if(objc == 5 && !strcmp(op, "empty")){
4800 		/*
4801 		 * CMD: empty
4802 		 *
4803 		 * Returns: number of expunge messages
4804 		 *
4805 		 * Arguments: <colnum> <folder> <what>
4806 		 *   where <what> is either <uid>, 'selected', or 'all'
4807 		 */
4808 		CONTEXT_S    *cp;
4809 		MAILSTREAM   *stream = NULL;
4810 		MESSAGECACHE *mc;
4811 		MSGNO_S	     *msgmap;
4812 		int	      colid, i, our_stream = 0;
4813 		long	      uid, raw, count = 0L;
4814 		char	     *errstr = NULL, *what, *folder, *p, tmp[MAILTMPLEN];
4815 
4816 	        if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4817 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4818 		      if(i == colid) break;
4819 		}
4820 
4821 		if(cp){
4822 		    if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4823 			if((what = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
4824 			    /* need to open? */
4825 			    if(!((context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4826 				  && (stream = same_stream_and_mailbox(tmp, wps_global->mail_stream)))
4827 				     || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){
4828 				long retflags = 0;
4829 
4830 				our_stream = 1;
4831 				stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags);
4832 			    }
4833 
4834 			    if(stream){
4835 				msgmap = sp_msgmap(stream);
4836 
4837 				if(!strucmp(what, "all")){
4838 				    if(mn_get_total(msgmap)){
4839 					agg_select_all(stream, msgmap, NULL, 1);
4840 					errstr = peApplyFlag(stream, msgmap, 'd', 0, &count);
4841 					if(!errstr)
4842 					  (void) cmd_expunge_work(stream, msgmap, NULL);
4843 				    }
4844 				}
4845 				else{
4846 				    /* little complicated since we don't display deleted state and
4847 				     * don't want to expunge what's not intended.
4848 				     * remember what's deleted and restore state on the ones left
4849 				     * when we're done. shouldn't happen much.
4850 				     * NOTE: "uid" is NOT a UID in this loop
4851 				     */
4852 				    for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
4853 					raw = mn_m2raw(msgmap, uid);
4854 					if(!get_lflag(stream, msgmap, uid, MN_EXLD)
4855 					   && (mc = mail_elt(stream, raw)) != NULL
4856 					   && mc->deleted){
4857 					    set_lflag(stream, msgmap, uid, MN_STMP, 1);
4858 					    mail_flag(stream, long2string(raw), "\\DELETED", 0L);
4859 					}
4860 					else
4861 					  set_lflag(stream, msgmap, uid, MN_STMP, 0);
4862 				    }
4863 
4864 				    if(!strucmp(what,"selected")){
4865 					if(any_lflagged(msgmap, MN_SLCT)){
4866 					    if(!(errstr = peApplyFlag(stream, msgmap, 'd', 0, &count)))
4867 					      (void) cmd_expunge_work(stream, msgmap, NULL);
4868 					}
4869 					else
4870 					  count = 0L;
4871 				    }
4872 				    else{
4873 					uid = 0;
4874 					for(p = what; *p; p++)
4875 					  if(isdigit((unsigned char) *p)){
4876 					      uid = (uid * 10) + (*p - '0');
4877 					  }
4878 					  else{
4879 					      errstr = "Invalid uid value";
4880 					      break;
4881 					  }
4882 
4883 					if(!errstr && uid){
4884 					    /* uid is a UID here */
4885 					    mail_flag(stream, long2string(uid), "\\DELETED", ST_SET | ST_UID);
4886 					    (void) cmd_expunge_work(stream, msgmap, NULL);
4887 					    count = 1L;
4888 					}
4889 				    }
4890 
4891 				    /* restore deleted on what didn't get expunged */
4892 				    for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
4893 					raw = mn_m2raw(msgmap, uid);
4894 					if(get_lflag(stream, msgmap, uid, MN_STMP)){
4895 					    set_lflag(stream, msgmap, uid, MN_STMP, 0);
4896 					    mail_flag(stream, long2string(raw), "\\DELETED", ST_SET);
4897 					}
4898 				    }
4899 				}
4900 
4901 				if(our_stream)
4902 				  pine_mail_close(stream);
4903 			    }
4904 			    else
4905 			      errstr = "no stream";
4906 			}
4907 			else
4908 			  errstr = "Cannot get which ";
4909 		    }
4910 		    else
4911 		      errstr = "Cannot get folder";
4912 		}
4913 		else
4914 		  errstr = "Invalid collection";
4915 
4916 		if(errstr){
4917 		    Tcl_SetResult(interp, errstr, TCL_VOLATILE);
4918 		    return(TCL_ERROR);
4919 		}
4920 
4921 		Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4922 		return(TCL_OK);
4923 	    }
4924 	    else if(!strcmp(op, "export")){
4925 		/*
4926 		 * CMD: export
4927 		 *
4928 		 * Returns: success or failure after writing given
4929 		 *          folder to given local file.
4930 		 *
4931 		 * Format:
4932 		 *    0) Collection Number
4933 		 *    1) Folder
4934 		 *    2) Destination file
4935 		 */
4936 		if(objc == 5){
4937 		    CONTEXT_S  *cp;
4938 		    MAILSTREAM *src;
4939 		    APPEND_PKG	pkg;
4940 		    STRING	msg;
4941 		    long	colid, i;
4942 		    char       *folder, *dfile, seq[64], tmp[MAILTMPLEN];
4943 		    int		our_stream = 0;
4944 
4945 		    if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4946 			for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4947 			  if(i == colid)
4948 			    break;
4949 
4950 			if(cp){
4951 			    if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4952 				if((dfile = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
4953 				    if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
4954 
4955 					snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile);
4956 
4957 					if(pine_mail_create(NULL, tmp)){
4958 
4959 					    err = NULL;		/* reset error condition */
4960 
4961 					    /*
4962 					     * if not current folder, open a stream, setup the
4963 					     * stuff to write the raw header/text by hand
4964 					     * with berkeley delimiters since we don't want
4965 					     * a local mailbox driver lunk in.
4966 					     *
4967 					     * comments:
4968 					     *  - BUG: what about logins?
4969 					     *
4970 					     */
4971 					    if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4972 						 && (src = same_stream_and_mailbox(tmp, wps_global->mail_stream)))){
4973 						long retflags = 0;
4974 
4975 						our_stream = 1;
4976 						src = context_open(cp, NULL, folder,
4977 								   SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE,
4978 								   &retflags);
4979 					    }
4980 
4981 					    if(src && src->nmsgs){
4982 						/* Go to work...*/
4983 						pkg.stream = src;
4984 						pkg.msgno  = 0;
4985 						pkg.msgmax = src->nmsgs;
4986 						pkg.flags = pkg.date = NIL;
4987 						pkg.message = &msg;
4988 
4989 						snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
4990 						mail_fetchfast (src, seq);
4991 
4992 						wps_global->noshow_error = 1;
4993 						if(!mail_append_multiple (NULL, dfile,
4994 									  peAppendMsg, (void *) &pkg)){
4995 						    snprintf(err = errbuf, sizeof(errbuf), "PEFolder: export: %.200s",
4996 							    wps_global->c_client_error);
4997 						}
4998 
4999 						wps_global->noshow_error = 0;
5000 
5001 						if(our_stream)
5002 						  pine_mail_close(src);
5003 					    }
5004 					    else
5005 					      err = "PEFolder: export: can't open mail folder";
5006 
5007 					    if(!err)
5008 					      return(TCL_OK);
5009 					}
5010 					else
5011 					  err = "PEFolder: export: can't create destination";
5012 
5013 					if(!mail_parameters(NULL, DISABLE_DRIVER, "unix"))
5014 					  err = "PEFolder: export: can't disable driver";
5015 				    }
5016 				    else
5017 				      err = "PEFolder: export: can't enable driver";
5018 				}
5019 				else
5020 				  err = "PEFolder: export: can't read file name";
5021 			    }
5022 			    else
5023 			      err = "PEFolder: export: can't read folder name";
5024 			}
5025 			else
5026 			  err = "PEFolder: export: Invalid collection ID";
5027 		    }
5028 		    else
5029 		      err = "PEFolder:export: Can't read collection ID";
5030 		}
5031 		else
5032 		  err = "PEFolder: export <colid> <folder> <file>";
5033 	    }
5034 	    else if(!strcmp(op, "import")){
5035 		/*
5036 		 * CMD: import
5037 		 *
5038 		 * Returns: success or failure after writing given
5039 		 *          folder to given local file.
5040 		 *
5041 		 * Format:
5042 		 *    0) source file
5043 		 *    1) destination collection number
5044 		 *    2) destination folder
5045 		 */
5046 		if(objc == 5){
5047 		    CONTEXT_S  *cp;
5048 		    MAILSTREAM *src, *dst;
5049 		    APPEND_PKG	pkg;
5050 		    STRING	msg;
5051 		    long	colid, i;
5052 		    char       *folder, *sfile, seq[64];
5053 
5054 		    /* get source file with a little sanity check */
5055 		    if((sfile = Tcl_GetStringFromObj(objv[2], NULL))
5056 		       && *sfile == '/' && !strstr(sfile, "..")){
5057 			if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
5058 
5059 			    wps_global->noshow_error = 1;	/* don't queue error msg */
5060 			    err = NULL;				/* reset error condition */
5061 
5062 			    /* make sure sfile contains valid mail */
5063 			    if((src = mail_open(NULL, sfile, 0L)) != NULL){
5064 
5065 				if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){
5066 				    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5067 				      if(i == colid)
5068 					break;
5069 
5070 				    if(cp){
5071 					if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
5072 					    long retflags = 0;
5073 
5074 					    if(context_create(cp, NULL, folder)
5075 					       && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){
5076 
5077 						if(src->nmsgs){
5078 						    /* Go to work...*/
5079 						    pkg.stream = src;
5080 						    pkg.msgno = 0;
5081 						    pkg.msgmax = src->nmsgs;
5082 						    pkg.flags = pkg.date = NIL;
5083 						    pkg.message = &msg;
5084 
5085 						    snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
5086 						    mail_fetchfast (src, seq);
5087 
5088 						    if(!context_append_multiple(cp, dst, folder,
5089 										peAppendMsg, (void *) &pkg,
5090 										wps_global->mail_stream)){
5091 							snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5092 								wps_global->c_client_error);
5093 						    }
5094 
5095 						}
5096 
5097 						pine_mail_close(dst);
5098 					    }
5099 					    else
5100 					      snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5101 						      wps_global->c_client_error);
5102 					}
5103 					else
5104 					  err = "PEFolder: import: can't read folder name";
5105 				    }
5106 				    else
5107 				      err = "PEFolder:import: invalid collection id";
5108 				}
5109 				else
5110 				  err = "PEFolder: import: can't read collection id";
5111 
5112 				mail_close(src);
5113 
5114 			    }
5115 			    else
5116 			      snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5117 				      wps_global->c_client_error);
5118 
5119 			    wps_global->noshow_error = 0;
5120 
5121 			    if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err)
5122 			      err = "PEFolder: import: can't disable driver";
5123 
5124 			    if(!err)
5125 			      return(TCL_OK);
5126 			}
5127 			else
5128 			  err = "PEFolder: import: can't enable driver";
5129 		    }
5130 		    else
5131 		      err = "PEFolder: import: can't read file name";
5132 		}
5133 		else
5134 		  err = "PEFolder: import <file> <colid> <folder>";
5135 	    }
5136 	    else {
5137 	      int	 i, colid;
5138 	      char	*aes, *colstr;
5139 	      CONTEXT_S *cp;
5140 
5141 	      /*
5142 	       * 3 or more arguments, 3rd is the collection ID, rest
5143 	       * are a folder name
5144 	       */
5145 
5146 	      if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
5147 		for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5148 		  if(i == colid) break;
5149 	      }
5150 	      else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
5151 		  if(!strcmp("default", colstr))
5152 		    cp = default_save_context(wps_global->context_list);
5153 		  else
5154 		    cp = NULL;
5155 	      }
5156 	      else
5157 		cp = NULL;
5158 
5159 	      if(cp){
5160 		if(!strcmp(op, "list")){
5161 		  int i, fcount, bflags = BFL_NONE;
5162 
5163 		  if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR)
5164 		    return TCL_ERROR;
5165 
5166 		  if(cp->use & CNTXT_NEWS)
5167 		    bflags |= BFL_LSUB;
5168 
5169 		  wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5170 
5171 		  pePrepareForAuthException();
5172 
5173 		  build_folder_list(NULL, cp, "*", NULL, bflags);
5174 
5175 		  if((aes = peAuthException()) != NULL){
5176 		      Tcl_SetResult(interp, aes, TCL_VOLATILE);
5177 		      reset_context_folders(cp);
5178 		      return(TCL_ERROR);
5179 		  }
5180 
5181 		  if((fcount = folder_total(FOLDERS(cp))) != 0){
5182 		    for(i = 0; i < fcount; i++){
5183 		      char type[3], *p;
5184 		      FOLDER_S *f = folder_entry(i, FOLDERS(cp));
5185 
5186 		      p = type;
5187 		      if(f->isdir){
5188 			  *p++ = 'D';
5189 
5190 			  if(f->hasnochildren && !f->haschildren)
5191 			    *p++ = 'E';
5192 		      }
5193 
5194 		      if(f->isfolder
5195 			 || f->nickname
5196 			 || (cp->use & CNTXT_INCMNG))
5197 			*p++ = 'F';
5198 
5199 		      *p = '\0';
5200 
5201 		      peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type,
5202 				 f->nickname ? f->nickname : f->name);
5203 		    }
5204 		  }
5205 
5206 		  reset_context_folders(cp);
5207 		  return(TCL_OK);
5208 		}
5209 		else if(!strucmp(op, "exists")){
5210 		  char *folder, *errstr = NULL;
5211 		  int   rv;
5212 
5213 		  if(objc < 4) {
5214 		    Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE);
5215 		    return(TCL_ERROR);
5216 		  }
5217 		  folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5218 		  if(!folder) {
5219 		    Tcl_SetResult(interp, "PEFolder exists: Can't read folder", TCL_VOLATILE);
5220 		    return(TCL_ERROR);
5221 		  }
5222 
5223 		  if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5224 		    return TCL_ERROR;
5225 
5226 		  wps_global->c_client_error[0] = '\0';
5227 		  pePrepareForAuthException();
5228 
5229 		  rv = folder_name_exists(cp, folder, NULL);
5230 
5231 		  if(rv & FEX_ERROR){
5232 		      if((errstr = peAuthException()) == NULL){
5233 			  if(wps_global->c_client_error[0])
5234 			    errstr = wps_global->c_client_error;
5235 			  else
5236 			    errstr = "Indeterminate Error";
5237 		      }
5238 		  }
5239 
5240 		  Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE);
5241 		  return(errstr ? TCL_ERROR : TCL_OK);
5242 		}
5243 		else if(!strucmp(op, "fullname")){
5244 		  char *folder, *fullname;
5245 
5246 		  if(objc < 4) {
5247 		    Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE);
5248 		    return(TCL_ERROR);
5249 		  }
5250 		  folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5251 		  if(!folder) {
5252 		    Tcl_SetResult(interp, "PEFolder fullname: Can't read folder", TCL_VOLATILE);
5253 		    return(TCL_ERROR);
5254 		  }
5255 
5256 		  if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5257 		    return TCL_ERROR;
5258 
5259 #if	0
5260 		  Tcl_Obj *obj = Tcl_NewStringObj((fullname = folder_is_nick(folder, FOLDERS(cp)))
5261 						  ? fullname : folder, -1);
5262 		  (void) Tcl_ListObjAppendElement(interp,
5263 						  Tcl_GetObjResult(interp),
5264 						  obj);
5265 #else
5266 		  Tcl_SetResult(interp,
5267 				(fullname = folder_is_nick(folder, FOLDERS(cp), FN_NONE)) ? fullname : folder,
5268 				TCL_VOLATILE);
5269 #endif
5270 
5271 		  return(TCL_OK);
5272 		}
5273 		else if(!strucmp(op, "create")){
5274 		    char *aes, *folder;
5275 
5276 		    folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5277 		    if(!folder) {
5278 			Tcl_SetResult(interp, "PEFolder create: Can't read folder", TCL_VOLATILE);
5279 			return(TCL_ERROR);
5280 		    }
5281 
5282 		    if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5283 		      return TCL_ERROR;
5284 
5285 		    wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5286 		    pePrepareForAuthException();
5287 
5288 		    if(!context_create(cp, NULL, folder)){
5289 			if((aes = peAuthException()) != NULL){
5290 			    Tcl_SetResult(interp, aes, TCL_VOLATILE);
5291 			}
5292 			else{
5293 			    Tcl_SetResult(interp,
5294 					  (wps_global->last_error[0])
5295 					    ? wps_global->last_error
5296 					    : (wps_global->c_client_error[0])
5297 					        ? wps_global->c_client_error
5298 					        : "Unable to create folder",
5299 					  TCL_VOLATILE);
5300 			}
5301 
5302 			reset_context_folders(cp);
5303 			return(TCL_ERROR);
5304 		    }
5305 
5306 		    Tcl_SetResult(interp, "OK", TCL_STATIC);
5307 		    reset_context_folders(cp);
5308 		    return(TCL_OK);
5309 		}
5310 		else if(!strucmp(op, "delete")){
5311 		    int		fi, readonly, close_opened = 0;
5312 		    char       *folder, *fnamep, *target = NULL, *aes;
5313 		    MAILSTREAM *del_stream = NULL, *strm = NULL;
5314 		    EditWhich   ew;
5315 		    PINERC_S   *prc = NULL;
5316 		    FOLDER_S   *fp;
5317 
5318 		    folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5319 		    if(!folder) {
5320 			Tcl_SetResult(interp, "PEFolder delete: Can't read folder", TCL_VOLATILE);
5321 			return(TCL_ERROR);
5322 		    }
5323 
5324 		    if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5325 		      return TCL_ERROR;
5326 
5327 		    /* so we can check for folder's various properties */
5328 		    build_folder_list(NULL, cp, folder, NULL, BFL_NONE);
5329 
5330 		    wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5331 
5332 		    pePrepareForAuthException();
5333 
5334 		    /* close open folder, then delete */
5335 
5336 		    if((fi = folder_index(folder, cp, FI_FOLDER)) < 0
5337 		       || (fp = folder_entry(fi, FOLDERS(cp))) == NULL){
5338 			Tcl_SetResult(interp, "Cannot find folder to delete", TCL_STATIC);
5339 			reset_context_folders(cp);
5340 			return(TCL_ERROR);
5341 		    }
5342 
5343 		    if(!((cp->use & CNTXT_INCMNG) && fp->name
5344 			 && check_for_move_mbox(fp->name, NULL, 0, &target))){
5345 			target = NULL;
5346 		    }
5347 
5348 		    dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?"));
5349 
5350 		    ew = config_containing_inc_fldr(fp);
5351 		    if(wps_global->restricted)
5352 		      readonly = 1;
5353 		    else{
5354 			switch(ew){
5355 			  case Main:
5356 			    prc = wps_global->prc;
5357 			    break;
5358 			  case Post:
5359 			    prc = wps_global->post_prc;
5360 			    break;
5361 			  case None:
5362 			    break;
5363 			}
5364 
5365 			readonly = prc ? prc->readonly : 1;
5366 		    }
5367 
5368 		    if(prc && prc->quit_to_edit && (cp->use & CNTXT_INCMNG)){
5369 			Tcl_SetResult(interp, "Must Exit Alpine to Change Configuration", TCL_STATIC);
5370 			reset_context_folders(cp);
5371 			return(TCL_ERROR);
5372 		    }
5373 
5374 		    if(cp == wps_global->context_list
5375 		       && !(cp->dir && cp->dir->ref)
5376 		       && strucmp(folder, wps_global->inbox_name) == 0){
5377 			Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC);
5378 			reset_context_folders(cp);
5379 			return(TCL_ERROR);
5380 		    }
5381 		    else if(readonly && (cp->use & CNTXT_INCMNG)){
5382 			Tcl_SetResult(interp, "Folder not in editable config file", TCL_STATIC);
5383 			reset_context_folders(cp);
5384 			return(TCL_ERROR);
5385 		    }
5386 		    else if((fp->name
5387 			     && (strm=context_already_open_stream(cp,fp->name,AOS_NONE)))
5388 			    ||
5389 			    (target
5390 			     && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
5391 			if(strm == wps_global->mail_stream)
5392 			  close_opened++;
5393 		    }
5394 		    else if(fp->isdir || fp->isdual){	/* NO DELETE if directory isn't EMPTY */
5395 			FDIR_S *fdirp = next_folder_dir(cp,folder,TRUE,NULL);
5396 			int     ret;
5397 
5398 			if(fp->haschildren)
5399 			  ret = 1;
5400 			else if(fp->hasnochildren)
5401 			  ret = 0;
5402 			else{
5403 			    ret = folder_total(fdirp->folders) > 0;
5404 			    free_fdir(&fdirp, 1);
5405 			}
5406 
5407 			if(ret){
5408 			    Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC);
5409 			    reset_context_folders(cp);
5410 			    return(TCL_ERROR);
5411 			}
5412 
5413 			/*
5414 			 * Folder by the same name exist, so delete both...
5415 			if(fp->isdual){
5416 			    Tcl_SetResult(interp, "Cannot delete: folder is also a directory", TCL_STATIC);
5417 			    reset_context_folders(cp);
5418 			    return(TCL_ERROR);
5419 			}
5420 			*/
5421 		    }
5422 
5423 		    if(cp->use & CNTXT_INCMNG){
5424 			Tcl_SetResult(interp, "Cannot delete incoming folder", TCL_STATIC);
5425 			reset_context_folders(cp);
5426 			return(TCL_ERROR);
5427 		    }
5428 
5429 		    dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
5430 			    fp->name ? fp->name : "?",
5431 			    fp->nickname ? fp->nickname : "",
5432 			    cp->context ? cp->context : "?"));
5433 		    if(strm){
5434 			/*
5435 			 * Close it, NULL the pointer, and let do_broach_folder fixup
5436 			 * the rest...
5437 			 */
5438 			pine_mail_actually_close(strm);
5439 			if(close_opened){
5440 			    do_broach_folder(wps_global->inbox_name,
5441 					     wps_global->context_list,
5442 					     NULL, DB_INBOXWOCNTXT);
5443 			}
5444 		    }
5445 
5446 		    /*
5447 		     * Use fp->name since "folder" may be a nickname...
5448 		     */
5449 		    if(wps_global->mail_stream
5450 		       && context_same_stream(cp, fp->name, wps_global->mail_stream))
5451 		      del_stream = wps_global->mail_stream;
5452 
5453 		    fnamep = fp->name;
5454 
5455 		    if(!context_delete(cp, del_stream, fnamep)){
5456 			if((aes = peAuthException()) != NULL){
5457 			    Tcl_SetResult(interp, aes, TCL_VOLATILE);
5458 			}
5459 			else{
5460 			    Tcl_SetResult(interp,
5461 					  (wps_global->last_error[0])
5462 					    ? wps_global->last_error
5463 					    : (wps_global->c_client_error[0])
5464 					      ? wps_global->c_client_error
5465 					      : "Unable to delete folder",
5466 					  TCL_VOLATILE);
5467 			}
5468 
5469 			reset_context_folders(cp);
5470 			return(TCL_ERROR);
5471 		    }
5472 
5473 
5474 		    Tcl_SetResult(interp, "OK", TCL_STATIC);
5475 		    reset_context_folders(cp);
5476 		    return(TCL_OK);
5477 		}
5478 		/*
5479 		 * must be at least 5 arguments for the next set of commands
5480 		 */
5481 		else if(objc < 5) {
5482 		  Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE);
5483 		  return(TCL_ERROR);
5484 		}
5485 		else if(!strucmp(op, "rename")){
5486 		    char *folder,*newfolder, *aes;
5487 
5488 		  folder = Tcl_GetStringFromObj(objv[objc - 2], NULL);
5489 		  if(!folder) {
5490 		    Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
5491 		    return(TCL_ERROR);
5492 		  }
5493 
5494 		  newfolder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5495 		  if(!newfolder) {
5496 		    Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
5497 		    return(TCL_ERROR);
5498 		  }
5499 
5500 		  if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR)
5501 		    return TCL_ERROR;
5502 
5503 		  wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5504 		  pePrepareForAuthException();
5505 
5506 		  if(!context_rename(cp, NULL, folder, newfolder)){
5507 		      if((aes = peAuthException()) != NULL){
5508 		      Tcl_SetResult(interp, aes, TCL_VOLATILE);
5509 		    }
5510 		    else{
5511 		      Tcl_SetResult(interp,
5512 				    (wps_global->last_error[0])
5513 				     ? wps_global->last_error
5514 				     : (wps_global->c_client_error[0])
5515 				        ? wps_global->c_client_error
5516 				        : "Unable to rename folder",
5517 				    TCL_VOLATILE);
5518 		    }
5519 		    reset_context_folders(cp);
5520 		    return(TCL_ERROR);
5521 		  }
5522 		  Tcl_SetResult(interp, "OK", TCL_STATIC);
5523 		  reset_context_folders(cp);
5524 		  return(TCL_OK);
5525 		}
5526 	      }
5527 	      else
5528 		err = "PEFolder: Unrecognized collection ID";
5529 	    }
5530 	}
5531 	else
5532 	  err = "No User Context Established";
5533     }
5534 
5535     Tcl_SetResult(interp, err, TCL_VOLATILE);
5536     return(TCL_ERROR);
5537 }
5538 
5539 
5540 /*
5541  * PEMailboxCmd - export various bits of mailbox information
5542  */
5543 int
PEMailboxCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5544 PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
5545 {
5546     char *op, errbuf[256], *err = "Unknown PEMailbox operation";
5547 
5548     dprint((5, "PEMailboxCmd"));
5549 
5550     if(objc == 1){
5551 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
5552     }
5553     else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
5554 	if(!strucmp(op, "open")){
5555 	    int	       i, colid;
5556 	    char      *folder;
5557 	    CONTEXT_S *cp;
5558 
5559 	    peED.uid = 0;		/* forget cached embedded data */
5560 
5561 	    /*
5562 	     * CMD: open <context-index> <folder>
5563 	     *
5564 	     *
5565 	     */
5566 	    if(objc == 2){
5567 		Tcl_SetResult(interp, (!sp_dead_stream(wps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE);
5568 		return(TCL_OK);
5569 	    }
5570 
5571 	    if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
5572 		if((folder = Tcl_GetStringFromObj(objv[objc - 1], NULL)) != NULL) {
5573 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5574 		      if(i == colid) {
5575 			  if(PEMakeFolderString(interp, cp, objc - 3, objv + 3,
5576 						&folder))
5577 			    return TCL_ERROR;
5578 
5579 			  dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder));
5580 
5581 			  return(peCreateStream(interp, cp, folder, FALSE));
5582 		      }
5583 
5584 		    err = "open: Unrecognized collection ID";
5585 		}
5586 		else
5587 		  err = "open: Can't read folder";
5588 	    }
5589 	    else
5590 	      err = "open: Can't get collection ID";
5591 	}
5592 	else if(!strcmp(op, "indexformat")){
5593 	    /*
5594 	     * CMD: indexformat
5595 	     *
5596 	     * Returns: list of lists where:
5597 	     *		*  the first element is the name of the
5598 	     *		   field which may be "From", "Subject"
5599 	     *		   "Date" or the empty string.
5600 	     *		*  the second element which is either
5601 	     *		   the percentage width or empty string
5602 	     */
5603 	    if(objc == 2)
5604 	      return(peIndexFormat(interp));
5605 	}
5606 	else if(wps_global && wps_global->mail_stream){
5607 	    if(!strcmp(op, "select")){
5608 		return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
5609 	    }
5610 	    else if(!strcmp(op, "search")){
5611 		return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
5612 	    }
5613 	    else if(!strucmp(op, "apply")){
5614 		return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2]));
5615 	    }
5616 	    else if(!strcmp(op, "expunge")){
5617 		/*
5618 		 * CMD: expunge
5619 		 *
5620 		 * Returns: OK after having removed deleted messages
5621 		 */
5622 		char       *streamstr = NULL;
5623 		MAILSTREAM *stream;
5624 		MSGNO_S    *msgmap;
5625 
5626 		if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5627 		if(!streamstr
5628 		   || (streamstr && (strcmp(streamstr, "current") == 0))){
5629 		    stream = wps_global->mail_stream;
5630 		    msgmap = sp_msgmap(stream);
5631 		}
5632 		else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5633 		    stream = sp_inbox_stream();
5634 		    msgmap = sp_msgmap(stream);
5635 		}
5636 		else return(TCL_ERROR);
5637 		wps_global->last_error[0] = '\0';
5638 		if(IS_NEWS(stream)
5639 		   && stream->rdonly){
5640 		    msgno_exclude_deleted(stream, msgmap, NULL);
5641 		    clear_index_cache(sp_inbox_stream(), 0);
5642 
5643 		    /*
5644 		     * This is kind of surprising at first. For most sort
5645 		     * orders, if the whole set is sorted, then any subset
5646 		     * is also sorted. Not so for OrderedSubject sort.
5647 		     * If you exclude the first message of a subject group
5648 		     * then you change the date that group is to be sorted on.
5649 		     */
5650 		    if(mn_get_sort(msgmap) == SortSubject2)
5651 		      refresh_sort(wps_global->mail_stream, msgmap, FALSE);
5652 		}
5653 		else
5654 		  (void) cmd_expunge_work(stream, msgmap, NULL);
5655 
5656 		Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
5657 		return(TCL_OK);
5658 	    }
5659 	    else if(!strcmp(op, "trashdeleted")){
5660 		/*
5661 		 * CMD: trashdeleted
5662 		 *
5663 		 * Returns: OK after moving deleted messages to Trash and expunging
5664 		 */
5665 		MAILSTREAM   *stream;
5666 		MESSAGECACHE *mc;
5667 		CONTEXT_S    *cp;
5668 		MSGNO_S	     *msgmap;
5669 		char	     *streamstr = NULL, tmp[MAILTMPLEN];
5670 		long	      n, tomove = 0L;
5671 
5672 		if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5673 		if(!streamstr
5674 		   || (streamstr && (strcmp(streamstr, "current") == 0))){
5675 		    stream = wps_global->mail_stream;
5676 		    msgmap = sp_msgmap(stream);
5677 		}
5678 		else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5679 		    stream = sp_inbox_stream();
5680 		    msgmap = sp_msgmap(stream);
5681 		}
5682 		else return(TCL_ERROR);
5683 
5684 		wps_global->last_error[0] = '\0';
5685 		if(IS_NEWS(stream) && stream->rdonly){
5686 		    msgno_exclude_deleted(stream, msgmap, NULL);
5687 		    clear_index_cache(sp_inbox_stream(), 0);
5688 
5689 		    /*
5690 		     * This is kind of surprising at first. For most sort
5691 		     * orders, if the whole set is sorted, then any subset
5692 		     * is also sorted. Not so for OrderedSubject sort.
5693 		     * If you exclude the first message of a subject group
5694 		     * then you change the date that group is to be sorted on.
5695 		     */
5696 		    if(mn_get_sort(msgmap) == SortSubject2)
5697 		      refresh_sort(wps_global->mail_stream, msgmap, FALSE);
5698 		}
5699 		else{
5700 		    if(!(cp = default_save_context(wps_global->context_list)))
5701 		      cp = wps_global->context_list;
5702 
5703 		    /* copy to trash if we're not in trash */
5704 		    if(wps_global->VAR_TRASH_FOLDER
5705 		       && wps_global->VAR_TRASH_FOLDER[0]
5706 		       && context_allowed(context_apply(tmp, cp, wps_global->VAR_TRASH_FOLDER, sizeof(tmp)))
5707 		       && !same_stream_and_mailbox(tmp, stream)){
5708 
5709 			/* save real selected set, and  */
5710 			for(n = 1L; n <= mn_get_total(msgmap); n++){
5711 			    set_lflag(stream, msgmap, n, MN_STMP, get_lflag(stream, msgmap, n, MN_SLCT));
5712 			    /* select deleted */
5713 			    if(!get_lflag(stream, msgmap, n, MN_EXLD)
5714 			       && (mc = mail_elt(stream, mn_m2raw(msgmap,n))) != NULL && mc->deleted){
5715 				tomove++;
5716 				set_lflag(stream, msgmap, n, MN_SLCT, 1);
5717 			    }
5718 			    else
5719 			      set_lflag(stream, msgmap, n, MN_SLCT, 0);
5720 			}
5721 
5722 			if(tomove && pseudo_selected(stream, msgmap)){
5723 
5724 			    /* save deleted to Trash */
5725 			    n = save(wps_global, stream,
5726 				     cp, wps_global->VAR_TRASH_FOLDER,
5727 				     msgmap, SV_FOR_FILT | SV_FIX_DELS);
5728 
5729 			    /* then remove them */
5730 			    if(n == tomove){
5731 				(void) cmd_expunge_work(stream, msgmap, NULL);
5732 			    }
5733 
5734 			    restore_selected(msgmap);
5735 			}
5736 
5737 			/* restore selected set */
5738 			for(n = 1L; n <= mn_get_total(msgmap); n++)
5739 			  set_lflag(stream, msgmap, n, MN_SLCT,
5740 				    get_lflag(stream, msgmap, n, MN_STMP));
5741 		    }
5742 		}
5743 
5744 		Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
5745 		return(TCL_OK);
5746 	    }
5747 	    else if(!strcmp(op, "nextvector")){
5748 		long	 msgno, count, countdown;
5749 		int	 i, aObjN = 0;
5750 		char	*errstr = NULL, *s;
5751 		Tcl_Obj *rvObj, *vObj, *avObj, **aObj;
5752 
5753 		/*
5754 		 * CMD: nextvector
5755 		 *
5756 		 * ARGS: msgno - message number "next" is relative to
5757 		 *       count - how many msgno slots to return
5758 		 *	     attrib  - (optional) attributes to be returned with each message in vector
5759 		 *
5760 		 * Returns: vector containing next <count> messagenumbers (and optional attributes)
5761 		 */
5762 		if(objc == 4 || objc == 5){
5763 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) == TCL_OK){
5764 			if(Tcl_GetLongFromObj(interp, objv[3], &count) == TCL_OK){
5765 
5766 			    /* set index range for efficiency */
5767 			    if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
5768 				gPeITop   = msgno;
5769 				gPeICount = count;
5770 			    }
5771 
5772 			    if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){
5773 
5774 				if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0
5775 				   && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))){
5776 				    mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
5777 
5778 				    for(countdown = count; countdown > 0; countdown--){
5779 					imapuid_t  uid = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), msgno));
5780 					int	   fetched = 0;
5781 
5782 					if((vObj = Tcl_NewListObj(0, NULL)) != NULL){
5783 					    Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno));
5784 					    peAppListF(interp, vObj, "%lu", uid);
5785 
5786 					    if(aObjN){
5787 						if((avObj = Tcl_NewListObj(0, NULL)) != NULL){
5788 						    for(i = 0; i < aObjN; i++){
5789 							if((s = Tcl_GetStringFromObj(aObj[i], NULL)) != NULL){
5790 							    if(!strcmp(s, "statusbits")){
5791 								char *s = peMsgStatBitString(wps_global, wps_global->mail_stream,
5792 											     sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
5793 											     gPeITop, gPeICount, &fetched);
5794 								Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1));
5795 							    }
5796 							    else if(!strcmp(s, "statuslist")){
5797 								Tcl_Obj *nObj = peMsgStatNameList(interp, wps_global, wps_global->mail_stream,
5798 												  sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
5799 												  gPeITop, gPeICount, &fetched);
5800 								Tcl_ListObjAppendElement(interp, avObj, nObj);
5801 							    }
5802 							    else if(!strcmp(s, "status")){
5803 								long	      raw;
5804 								char	      stat[3];
5805 								MESSAGECACHE *mc;
5806 
5807 								raw = peSequenceNumber(uid);
5808 
5809 								if(!((mc = mail_elt(wps_global->mail_stream, raw)) && mc->valid)){
5810 								    mail_fetch_flags(wps_global->mail_stream,
5811 										     ulong2string(uid), FT_UID);
5812 								    mc = mail_elt(wps_global->mail_stream, raw);
5813 								}
5814 
5815 								stat[0] = mc->deleted ? '1' : '0';
5816 								stat[1] = mc->recent ? '1' : '0';
5817 								stat[2] = mc->seen ? '1' : '0';
5818 
5819 								Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3));
5820 							    }
5821 							    else if(!strcmp(s, "indexparts")){
5822 								Tcl_Obj *iObj;
5823 
5824 								if((iObj = Tcl_NewListObj(0, NULL)) != NULL
5825 								   && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK
5826 								   && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){
5827 								}
5828 								else
5829 								  return(TCL_ERROR);
5830 							    }
5831 							    else if(!strucmp(s, "indexcolor")){
5832 								if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK)
5833 								  return(TCL_ERROR);
5834 							    }
5835 							}
5836 							else{
5837 							    errstr = "nextvector: can't read attributes";
5838 							    break;
5839 							}
5840 						    }
5841 
5842 						    Tcl_ListObjAppendElement(interp, vObj, avObj);
5843 						}
5844 						else{
5845 						    errstr = "nextvector: can't allocate attribute return vector";
5846 						    break;
5847 						}
5848 					    }
5849 					}
5850 					else{
5851 					    errstr = "nextvector: can't allocate new vector";
5852 					    break;
5853 					}
5854 
5855 					Tcl_ListObjAppendElement(interp, rvObj, vObj);
5856 
5857 					for(++msgno; msgno <= mn_get_total(sp_msgmap(wps_global->mail_stream)) && msgline_hidden(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_NONE); msgno++)
5858 					  ;
5859 
5860 					if(msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))
5861 					  break;
5862 				    }
5863 				}
5864 
5865 				if(!errstr){
5866 				    /* append result vector */
5867 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), rvObj);
5868 				    /* Everything is coerced to UTF-8 */
5869 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
5870 							     Tcl_NewStringObj("UTF-8", -1));
5871 				    return(TCL_OK);
5872 				}
5873 			    }
5874 			    else
5875 			      errstr = "nextvector: can't read attribute list";
5876 			}
5877 			else
5878 			  errstr = "nextvector: can't read count";
5879 		    }
5880 		    else
5881 		      errstr = "nextvector: can't read message number";
5882 		}
5883 		else
5884 		  errstr = "nextvector: Incorrect number of arguments";
5885 
5886 		if(errstr)
5887 		  Tcl_SetResult(interp, errstr, TCL_STATIC);
5888 
5889 		return(TCL_ERROR);
5890 	    }
5891 	    else if(objc == 2){
5892 		if(!strcmp(op, "messagecount")){
5893 		    /*
5894 		     * CMD: messagecount
5895 		     *
5896 		     * Returns: count of messages in open mailbox
5897 		     */
5898 		    Tcl_SetResult(interp,
5899 				  long2string(mn_get_total(sp_msgmap(wps_global->mail_stream))),
5900 				  TCL_VOLATILE);
5901 		    return(TCL_OK);
5902 		}
5903 		else if(!strcmp(op, "firstinteresting")){
5904 		    /*
5905 		     * CMD: firstinteresting
5906 		     *
5907 		     * Returns: message number associated with
5908 		     *		"incoming-startup-rule" which had better
5909 		     *		be the "current" message since it was set
5910 		     *		in do_broach_folder and shouldn't have been
5911 		     *		changed otherwise (expunged screw us?)
5912 		     */
5913 		    Tcl_SetResult(interp,
5914 				  long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
5915 				  TCL_VOLATILE);
5916 		    return(TCL_OK);
5917 		}
5918 		else if(!strcmp(op, "selected")){
5919 		    /*
5920 		     * CMD: selected
5921 		     *
5922 		     * Returns: count of selected messages in open mailbox
5923 		     */
5924 
5925 		    Tcl_SetResult(interp,
5926 				  long2string(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT)),
5927 				  TCL_VOLATILE);
5928 		    return(TCL_OK);
5929 		}
5930 		else if(!strcmp(op, "searched")){
5931 		    /*
5932 		     * CMD: searched
5933 		     *
5934 		     * Returns: count of searched messages in open mailbox
5935 		     */
5936 
5937 		    Tcl_SetResult(interp,
5938 				  long2string(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH)),
5939 				  TCL_VOLATILE);
5940 		    return(TCL_OK);
5941 		}
5942 		else if(!strcmp(op, "mailboxname")){
5943 		    /*
5944 		     * CMD: name
5945 		     *
5946 		     * Returns: string representing the name of the
5947 		     *		current mailbox
5948 		     */
5949 		    Tcl_SetResult(interp, wps_global->cur_folder, TCL_VOLATILE);
5950 		    return(TCL_OK);
5951 		}
5952 		else if(!strcmp(op, "close")){
5953 		    /*
5954 		     * CMD: close
5955 		     *
5956 		     * Returns: with global mail_stream closed
5957 		     */
5958 		    peDestroyStream(wps_global);
5959 		    return(TCL_OK);
5960 		}
5961 		else if(!strcmp(op, "newmailreset")){
5962 		    sml_seen();
5963 		    zero_new_mail_count();
5964 		    sp_set_mail_box_changed(wps_global->mail_stream, 0);
5965 		    sp_set_expunge_count(wps_global->mail_stream, 0L);
5966 		    peMarkInputTime();
5967 		    return(TCL_OK);
5968 		}
5969 		else if(!strcmp(op, "newmailstatmsg")){
5970 		    long newest, count;
5971 		    char subject[500], subjtxt[500], from[500], intro[500], *s = "";
5972 
5973 		    /*
5974 		     * CMD: newmailstatmsg
5975 		     *
5976 		     * ARGS: none
5977 		     *
5978 		     * Returns: text for new mail message
5979 		     *
5980 		     */
5981 
5982 		    if(sp_mail_box_changed(wps_global->mail_stream)
5983 		       && (count = sp_mail_since_cmd(wps_global->mail_stream))){
5984 
5985 			for(newest = wps_global->mail_stream->nmsgs; newest > 1L; newest--)
5986 			  if(!get_lflag(wps_global->mail_stream, NULL, newest, MN_EXLD))
5987 			    break;
5988 
5989 			if(newest){
5990 			    format_new_mail_msg(NULL, count,
5991 						pine_mail_fetchstructure(wps_global->mail_stream,
5992 								    newest, NULL),
5993 						intro, from, subject, subjtxt, sizeof(subject));
5994 
5995 			    snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt);
5996 			}
5997 		    }
5998 
5999 		    Tcl_SetResult(interp, s, TCL_VOLATILE);
6000 		    return(TCL_OK);
6001 		}
6002 		else if(!strcmp(op, "savedefault")){
6003 		    return(peSaveDefault(interp, 0L, 0, NULL));
6004 		}
6005 		else if(!strcmp(op, "gotodefault")){
6006 		    return(peGotoDefault(interp, 0L, NULL));
6007 		}
6008 		else if(!strcmp(op, "zoom")){
6009 		    Tcl_SetResult(interp,
6010 				  long2string((any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L)
6011 					        ? any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT) : 0L),
6012 				  TCL_VOLATILE);
6013 		    return(TCL_OK);
6014 		}
6015 		else if(!strcmp(op, "focus")){
6016 		    Tcl_SetResult(interp,
6017 				  long2string((any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L)
6018 					        ? any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH) : 0L),
6019 				  TCL_VOLATILE);
6020 		    return(TCL_OK);
6021 		}
6022 		else if(!strcmp(op, "first")){
6023 		    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE)){
6024 			long n;
6025 
6026 			for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++)
6027 			  if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE)){
6028 			      Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6029 			      return(TCL_OK);
6030 			  }
6031 
6032 			unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6033 
6034 		    }
6035 
6036 		    Tcl_SetResult(interp, int2string(1), TCL_VOLATILE);
6037 		    return(TCL_OK);
6038 		}
6039 		else if(!strucmp(op, "current")){
6040 		    long	  n = 0;
6041 		    unsigned long u = 0;
6042 
6043 		    /*
6044 		     * CMD: current
6045 		     *
6046 		     * ARGS:
6047 		     *
6048 		     * Returns: list of current msg {<sequence> <uid>}
6049 		     */
6050 
6051 		    if(mn_total_cur(sp_msgmap(wps_global->mail_stream)) <= 0
6052 		       || ((n = mn_get_cur(sp_msgmap(wps_global->mail_stream))) > 0
6053 			   && (u = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), n))) > 0)){
6054 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
6055 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(u), -1));
6056 			return(TCL_OK);
6057 		    }
6058 		    else
6059 		      err = "Cannot get current";
6060 		}
6061 		else if(!strcmp(op, "last")){
6062 		    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE)){
6063 			long n;
6064 
6065 			for(n = mn_get_total(sp_msgmap(wps_global->mail_stream)); n > 0L; n--)
6066 			  if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE)){
6067 			      Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6068 			      return(TCL_OK);
6069 			  }
6070 		    }
6071 		    else{
6072 			Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6073 			return(TCL_OK);
6074 		    }
6075 
6076 		    Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC);
6077 		    return(TCL_ERROR);
6078 		}
6079 		else if(!strucmp(op, "sortstyles")){
6080 		    int	       i;
6081 		    /*
6082 		     * CMD: sortstyles
6083 		     *
6084 		     * Returns: list of supported sort styles
6085 		     */
6086 
6087 		    for(i = 0; wps_global->sort_types[i] != EndofList; i++)
6088 		      if(Tcl_ListObjAppendElement(interp,
6089 						  Tcl_GetObjResult(interp),
6090 						  Tcl_NewStringObj(sort_name(wps_global->sort_types[i]), -1)) != TCL_OK)
6091 			return(TCL_ERROR);
6092 
6093 		    return(TCL_OK);
6094 		}
6095 		else if(!strucmp(op, "sort")){
6096 		    return(peAppendCurrentSort(interp));
6097 		}
6098 		else if(!strucmp(op, "state")){
6099 		    if(!wps_global->mail_stream || sp_dead_stream(wps_global->mail_stream))
6100 		      Tcl_SetResult(interp, "closed", TCL_STATIC);
6101 		    else if(wps_global->mail_stream->rdonly && !IS_NEWS(wps_global->mail_stream))
6102 		      Tcl_SetResult(interp, "readonly", TCL_STATIC);
6103 		    else
6104 		      Tcl_SetResult(interp, "ok", TCL_STATIC);
6105 
6106 		    return(TCL_OK);
6107 		}
6108 		else if(!strucmp(op, "excludedeleted")){
6109 		    msgno_exclude_deleted(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), NULL);
6110 		    return(TCL_OK);
6111 		}
6112 	    }
6113 	    else if(objc == 3){
6114 		if(!strcmp(op, "uid")){
6115 		    long msgno, raw;
6116 
6117 		    /*
6118 		     * Return uid of given message number
6119 		     *
6120 		     * CMD: uid <msgnumber>
6121 		     */
6122 
6123 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK)
6124 		      return(TCL_ERROR); /* conversion problem? */
6125 
6126 		    if((raw = mn_m2raw(sp_msgmap(wps_global->mail_stream), msgno)) > 0L){
6127 			raw = mail_uid(wps_global->mail_stream, raw);
6128 			Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE);
6129 			return(TCL_OK);
6130 		    }
6131 
6132 		    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno);
6133 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
6134 		    return(TCL_ERROR);
6135 		}
6136 		else if(!strcmp(op, "newmail")){
6137 		    int		  reload, force = UFU_NONE, rv;
6138 		    time_t	  now = time(0);
6139 
6140 		    /*
6141 		     * CMD: newmail
6142 		     *
6143 		     * ARGS: reload -- "1" if we're reloading
6144 		     *       (vs. just checking newmail as a side effect
6145 		     *        of building a new page)
6146 		     *
6147 		     * Returns: count -
6148 		     *          mostrecent -
6149 		     */
6150 		    if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
6151 		      reload = 0;
6152 
6153 		    /* minimum 10 second between IMAP pings */
6154 		    if(!time_of_last_input() || now - time_of_last_input() > 10){
6155 			force = UFU_FORCE;
6156 			peMarkInputTime();
6157 		    }
6158 
6159 		    /* check for new mail */
6160 		    new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE);
6161 
6162 		    rv = peNewMailResult(interp);
6163 
6164 		    if(!reload)				/* announced */
6165 		      zero_new_mail_count();
6166 
6167 		    return(rv);
6168 		}
6169 		else if(!strcmp(op, "flagcount")){
6170 		    char     *flag;
6171 		    long      count = 0L;
6172 		    long      flags = 0L;
6173 		    int	      objlc;
6174 		    Tcl_Obj **objlv;
6175 
6176 
6177 		    /*
6178 		     * CMD: flagcount
6179 		     *
6180 		     * ARGS: flags -
6181 		     *
6182 		     * Returns: count - number of message thusly flagged
6183 		     *          mostrecent -
6184 		     */
6185 		    if(Tcl_ListObjGetElements(interp, objv[2], &objlc, &objlv) == TCL_OK){
6186 			while(objlc--)
6187 			  if((flag = Tcl_GetStringFromObj(*objlv++, NULL)) != NULL){
6188 			      if(!strucmp(flag, "deleted")){
6189 				  flags |= F_DEL;
6190 			      }
6191 			      if(!strucmp(flag, "undeleted")){
6192 				  flags |= F_UNDEL;
6193 			      }
6194 			      else if(!strucmp(flag, "seen")){
6195 				  flags |= F_SEEN;
6196 			      }
6197 			      else if(!strucmp(flag, "unseen")){
6198 				  flags |= F_UNSEEN;
6199 			      }
6200 			      else if(!strucmp(flag, "flagged")){
6201 				  flags |= F_FLAG;
6202 			      }
6203 			      else if(!strucmp(flag, "unflagged")){
6204 				  flags |= F_UNFLAG;
6205 			      }
6206 			      else if(!strucmp(flag, "answered")){
6207 				  flags |= F_ANS;
6208 			      }
6209 			      else if(!strucmp(flag, "unanswered")){
6210 				  flags |= F_UNANS;
6211 			      }
6212 			      else if(!strucmp(flag, "recent")){
6213 				  flags |= F_RECENT;
6214 			      }
6215 			      else if(!strucmp(flag, "unrecent")){
6216 				  flags |= F_UNRECENT;
6217 			      }
6218 			  }
6219 
6220 			if(flags)
6221 			  count = count_flagged(wps_global->mail_stream, flags);
6222 		    }
6223 
6224 		    Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6225 		    return(TCL_OK);
6226 		}
6227 		else if(!strcmp(op, "zoom")){
6228 		    int  newstate;
6229 		    long n, zoomed = 0L;
6230 
6231 		    /*
6232 		     * CMD: zoom
6233 		     *
6234 		     *    Set/clear HID bits of non SLCT messages as requested.
6235 		     *    PEMailbox [first | last | next] are sensitive to these flags.
6236 		     *
6237 		     * ARGS: newstate - 1 or 0
6238 		     *
6239 		     * Returns: count of zoomed messages
6240 		     */
6241 
6242 		    if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6243 			if(newstate > 0){
6244 			    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(wps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT)))){
6245 				zoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MN_SLCT);
6246 				zoomed = n;
6247 			    }
6248 			}
6249 			else{
6250 			    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
6251 			      unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6252 			}
6253 		    }
6254 
6255 		    Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6256 		    return(TCL_OK);
6257 		}
6258 		else if(!strcmp(op, "focus")){
6259 		    int  newstate;
6260 		    long n, zoomed = 0L;
6261 
6262 		    /*
6263 		     * CMD: focus
6264 		     *
6265 		     *    Set/clear HID bits of non MN_SRCH messages as requested.
6266 		     *    PEMailbox [first | last | next] are sensitive to MN_HIDE flag
6267 		     *
6268 		     * ARGS: newstate - 1 or 0
6269 		     *
6270 		     * Returns: count of zoomed messages
6271 		     */
6272 
6273 		    if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6274 			if(newstate > 0){
6275 			    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(wps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH))))
6276 			      zoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MN_SRCH);
6277 
6278 			    zoomed = n;
6279 			}
6280 			else{
6281 			    if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
6282 			      unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6283 			}
6284 		    }
6285 
6286 		    Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6287 		    return(TCL_OK);
6288 		}
6289 		else if(!strcmp(op, "next")){
6290 		    long  msgno;
6291 
6292 		    /*
6293 		     * CMD: next <msgno>
6294 		     *
6295 		     * ARGS: msgno - message number "next" is relative to
6296 		     *
6297 		     * Returns: previous state
6298 		     */
6299 
6300 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){
6301 			mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6302 			mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6303 			Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6304 			return(TCL_OK);
6305 		    }
6306 
6307 		    Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6308 		    return(TCL_ERROR);
6309 		}
6310 	    }
6311 	    else if(objc == 4){
6312 		if(!strucmp(op, "sort")){
6313 		    int	       i, reversed = 0;
6314 		    char      *sort;
6315 
6316 		    /*
6317 		     * CMD: sort sortstyle reversed
6318 		     *
6319 		     * Returns: OK with the side-effect of message
6320 		     *          numbers now reflecting the requested
6321 		     *          sort order.
6322 		     */
6323 
6324 		    if((sort = Tcl_GetStringFromObj(objv[2], NULL))
6325 		       && Tcl_GetIntFromObj(interp, objv[3], &reversed) != TCL_ERROR){
6326 			/* convert sort string into */
6327 			for(i = 0; wps_global->sort_types[i] != EndofList; i++)
6328 			  if(strucmp(sort_name(wps_global->sort_types[i]), sort) == 0){
6329 			      if(sp_unsorted_newmail(wps_global->mail_stream)
6330 				 || !(wps_global->sort_types[i] == mn_get_sort(sp_msgmap(wps_global->mail_stream))
6331 				      && mn_get_revsort(sp_msgmap(wps_global->mail_stream)) == reversed))
6332 				sort_folder(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
6333 					    wps_global->sort_types[i],
6334 					    reversed, 0);
6335 
6336 			      break;
6337 			  }
6338 		    }
6339 
6340 		    return(peAppendCurrentSort(interp));
6341 		}
6342 		else if(!strucmp(op, "selected")){
6343 		    return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
6344 		}
6345 		else if(!strucmp(op, "searched")){
6346 		    return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
6347 		}
6348 		else if(!strcmp(op, "next")){
6349 		    long  msgno, count;
6350 
6351 		    /*
6352 		     * CMD: next
6353 		     *
6354 		     * ARGS: msgno - message number "next" is relative to
6355 		     *       count - how many to increment it
6356 		     *
6357 		     * Returns: previous state
6358 		     */
6359 
6360 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6361 		       && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6362 			mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6363 			while(count)
6364 			  if(count > 0){
6365 			      mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6366 			      count--;
6367 			  }
6368 			  else{
6369 			      mn_dec_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6370 			      count++;
6371 			  }
6372 
6373 			Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6374 			return(TCL_OK);
6375 		    }
6376 
6377 		    Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6378 		    return(TCL_ERROR);
6379 		}
6380 		else if(!strcmp(op, "x-nextvector")){
6381 		    long     msgno, count;
6382 
6383 		    /*
6384 		     * CMD: nextvector
6385 		     *
6386 		     * ARGS: msgno - message number "next" is relative to
6387 		     *       count - how many msgno slots to return
6388 		     *
6389 		     * Returns: vector containing next <count> messagenumbers
6390 		     */
6391 
6392 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6393 		       && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6394 			if(count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))){
6395 			    mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6396 
6397 			    while(count--){
6398 				long n = mn_get_cur(sp_msgmap(wps_global->mail_stream));
6399 
6400 				if(peAppListF(interp, Tcl_GetObjResult(interp),
6401 					      "%l%l", n, mail_uid(wps_global->mail_stream,
6402 							      mn_m2raw(sp_msgmap(wps_global->mail_stream), n))) != TCL_OK)
6403 				  return(TCL_ERROR);
6404 
6405 				mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6406 
6407 				if(n == mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6408 				  break;
6409 			    }
6410 			}
6411 
6412 			return(TCL_OK);
6413 		    }
6414 
6415 		    Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6416 		    return(TCL_ERROR);
6417 		}
6418 		else if(!strcmp(op, "messagecount")){
6419 		    char *relative;
6420 		    long  msgno, n, count = 0L;
6421 
6422 		    /*
6423 		     * CMD: messagecount
6424 		     *
6425 		     * ARGS: [before | after] relative to
6426 		     *       msgno
6427 		     *
6428 		     * Returns: count of messages before or after given message number
6429 		     */
6430 
6431 		    if((relative = Tcl_GetStringFromObj(objv[2], NULL))
6432 		       && Tcl_GetLongFromObj(interp, objv[3], &msgno) != TCL_ERROR){
6433 			if(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream))){
6434 			    Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC);
6435 			    return(TCL_ERROR);
6436 			}
6437 
6438 			if(!strucmp(relative, "before")){
6439 			    for(n = msgno - 1; n > 0L; n--)
6440 			      if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE))
6441 				count++;
6442 
6443 			    Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6444 			    return(TCL_OK);
6445 			}
6446 			else if(!strucmp(relative, "after")){
6447 			    for(n = msgno + 1; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++)
6448 			      if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE))
6449 				count++;
6450 
6451 			    Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6452 			    return(TCL_OK);
6453 			}
6454 		    }
6455 
6456 		    Tcl_SetResult(interp, "can't read range for count", TCL_STATIC);
6457 		    return(TCL_ERROR);
6458 		}
6459 		else if(!strcmp(op, "selectvector")){
6460 		    long     msgno, count;
6461 
6462 		    /*
6463 		     * CMD: selectvector
6464 		     *
6465 		     * ARGS: msgno - message number "next" is relative to
6466 		     *       count - how many msgno slots to return
6467 		     *
6468 		     * Returns: vector containing next <count> messagenumbers
6469 		     */
6470 
6471 		    if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6472 		       && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6473 			if(msgno > 0L){
6474 			    mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6475 			    while(count--){
6476 				msgno = mn_get_cur(sp_msgmap(wps_global->mail_stream));
6477 
6478 				if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_SLCT))
6479 				  if(Tcl_ListObjAppendElement(interp,
6480 							      Tcl_GetObjResult(interp),
6481 							      Tcl_NewLongObj((long) mail_uid(wps_global->mail_stream, msgno))) != TCL_OK)
6482 				    return(TCL_ERROR);
6483 
6484 				mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6485 
6486 				if(msgno == mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6487 				  break;
6488 			    }
6489 			}
6490 
6491 			return(TCL_OK);
6492 		    }
6493 
6494 		    Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC);
6495 		    return(TCL_ERROR);
6496 		}
6497 		else if(!strucmp(op, "current")){
6498 		    char	  *which;
6499 		    long	   x, n = 0, u = 0;
6500 
6501 		    /*
6502 		     * CMD: current
6503 		     *
6504 		     * ARGS: (number|uid) <msgno>
6505 		     *
6506 		     * Returns: list of current msg {<sequence> <uid>}
6507 		     */
6508 		    if((which = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
6509 			if(Tcl_GetLongFromObj(interp, objv[3], &x) == TCL_OK){
6510 			    if(!strucmp(which,"uid")){
6511 				u = x;
6512 				n = peMessageNumber(u);
6513 			    }
6514 			    else if(!strucmp(which,"number")){
6515 				n = x;
6516 				u = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), n));
6517 			    }
6518 
6519 			    if(n && u){
6520 				mn_set_cur(sp_msgmap(wps_global->mail_stream), n);
6521 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
6522 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(u), -1));
6523 				return(TCL_OK);
6524 			    }
6525 			    else
6526 			      err = "PEMailbox current: invalid number/uid";
6527 			}
6528 			else
6529 			  err = "PEMailbox current: cannot get number";
6530 		    }
6531 		    else
6532 		      err = "PEMailbox current: cannot get which";
6533 		}
6534 	    }
6535 	    else
6536 	      err = "PEMailbox: Too many arguments";
6537 	}
6538 	else if(!strucmp(op, "name") || !strcmp(op, "close")){
6539 	    Tcl_SetResult(interp, "", TCL_STATIC);
6540 	    return(TCL_OK);
6541 	}
6542 	else
6543 	  snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox",
6544 		   Tcl_GetStringFromObj(objv[0], NULL), op);
6545     }
6546 
6547     Tcl_SetResult(interp, err, TCL_VOLATILE);
6548     return(TCL_ERROR);
6549 }
6550 
6551 
6552 int
peAppendCurrentSort(Tcl_Interp * interp)6553 peAppendCurrentSort(Tcl_Interp *interp)
6554 {
6555     return((Tcl_ListObjAppendElement(interp,
6556 				     Tcl_GetObjResult(interp),
6557 				     Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(wps_global->mail_stream))), -1)) == TCL_OK
6558 	    && Tcl_ListObjAppendElement(interp,
6559 					Tcl_GetObjResult(interp),
6560 					Tcl_NewStringObj(mn_get_revsort(sp_msgmap(wps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK)
6561 	     ? TCL_OK : TCL_ERROR);
6562 }
6563 
6564 
6565 int
peAppendDefaultSort(Tcl_Interp * interp)6566 peAppendDefaultSort(Tcl_Interp *interp)
6567 {
6568     return((Tcl_ListObjAppendElement(interp,
6569 				     Tcl_GetObjResult(interp),
6570 				     Tcl_NewStringObj(sort_name(wps_global->def_sort), -1)) == TCL_OK
6571 	    && Tcl_ListObjAppendElement(interp,
6572 					Tcl_GetObjResult(interp),
6573 					Tcl_NewStringObj(wps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK)
6574 	     ? TCL_OK : TCL_ERROR);
6575 }
6576 
6577 
6578 int
peSelect(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)6579 peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6580 {
6581     char	 *subcmd;
6582     long	  n, i, diff, msgno;
6583     int		  narrow, hidden;
6584     MESSAGECACHE *mc;
6585     extern     MAILSTREAM *mm_search_stream;
6586     extern     long	   mm_search_count;
6587 
6588     hidden           = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L;
6589     mm_search_stream = wps_global->mail_stream;
6590     mm_search_count  = 0L;
6591 
6592     for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6593       if((mc = mail_elt(wps_global->mail_stream, n)) != NULL){
6594 	  mc->searched = 0;
6595 	  mc->spare7 = 1;
6596       }
6597 
6598     /*
6599      * CMD: select
6600      *
6601      * ARGS: subcmd subcmdargs
6602      *
6603      * Returns: flip "matchflag" private bit on all or none
6604      *          of the messages in the mailbox
6605      */
6606     if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
6607 	if(!strucmp(subcmd, "all")){
6608 	    /*
6609 	     * Args: <none>
6610 	     */
6611 	    if(matchflag & MN_SLCT){
6612 		if(objc != 1)
6613 		  return(peSelectError(interp, subcmd));
6614 
6615 		agg_select_all(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), NULL, 1);
6616 	    }
6617 	    else if(matchflag & MN_SRCH){
6618 		for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6619 		  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, matchflag, 1);
6620 	    }
6621 
6622 	    Tcl_SetResult(interp, "All", TCL_VOLATILE);
6623 	}
6624 	else if(!strucmp(subcmd, "none")){
6625 	    /*
6626 	     * Args: <none>
6627 	     */
6628 	    n = 0L;
6629 
6630 	    if(matchflag & MN_SLCT){
6631 		if(objc != 1)
6632 		  return(peSelectError(interp, subcmd));
6633 
6634 		agg_select_all(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), &n, 0);
6635 	    }
6636 	    else if(matchflag & MN_SRCH){
6637 		for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6638 		  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, matchflag, 0);
6639 	    }
6640 
6641 	    Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6642 	}
6643 	else if(!strucmp(subcmd, "searched")){
6644 	    /*
6645 	     * Args: <none>
6646 	     */
6647 	    for(n = 1L, i = 0; n <= wps_global->mail_stream->nmsgs; n++)
6648 	      if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SRCH)){
6649 		  i++;
6650 		  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SLCT, 1);
6651 	      }
6652 
6653 	    Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
6654 	}
6655 	else if(!strucmp(subcmd, "unsearched")){
6656 	    /*
6657 	     * Args: <none>
6658 	     */
6659 	    for(n = 1L, i = 0; n <= wps_global->mail_stream->nmsgs; n++)
6660 	      if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SRCH)){
6661 		  i++;
6662 		  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SLCT, 0);
6663 	      }
6664 
6665 	    Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
6666 	}
6667 	else{
6668 	    if(!strucmp(subcmd, "narrow"))
6669 	      narrow = 1;
6670 	    else if(!strucmp(subcmd, "broad"))
6671 	      narrow = 0;
6672 	    else
6673 	      return(peSelectError(interp, "invalid scope request"));
6674 
6675 	    if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL)))
6676 	      return(peSelectError(interp, "missing subcommand"));
6677 
6678 	    if(!strucmp(subcmd, "num")){
6679 		if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6680 		  return(i);
6681 	    }
6682 	    else if(!strucmp(subcmd, "date")){
6683 		if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6684 		  return(i);
6685 	    }
6686 	    else if(!strucmp(subcmd, "text")){
6687 		if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6688 		  return(i);
6689 	    }
6690 	    else if(!strucmp(subcmd, "status")){
6691 		if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6692 		  return(i);
6693 	    }
6694 	    else if(!strucmp(subcmd, "compound")){
6695 		char	 *s;
6696 		int	  nSearchList, nSearch;
6697 		Tcl_Obj **oSearchList, **oSearch;
6698 
6699 		/* BUG: should set up one SEARCHPGM to fit criteria and issue single search */
6700 
6701 		if(Tcl_ListObjGetElements(interp, objv[2], &nSearchList, &oSearchList) == TCL_OK){
6702 		    for(i = 0; i < nSearchList; i++){
6703 			if(Tcl_ListObjGetElements(interp, oSearchList[i], &nSearch, &oSearch) == TCL_OK){
6704 			    if((s = Tcl_GetStringFromObj(oSearch[0], NULL)) != NULL){
6705 				if(!strucmp(s,"date")){
6706 				    if((n = peSelectDate(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6707 				      return(n);
6708 				}
6709 				else if(!strucmp(s,"text")){
6710 				    if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6711 				      return(n);
6712 				}
6713 				else if(!strucmp(s,"status")){
6714 				    if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6715 				      return(n);
6716 				}
6717 				else
6718 				  return(peSelectError(interp, "unknown compound search"));
6719 
6720 				/* logical AND the results */
6721 				mm_search_count = 0L;
6722 				for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6723 				  if((mc = mail_elt(wps_global->mail_stream, n)) != NULL){
6724 				      if(mc->searched && mc->spare7)
6725 					mm_search_count++;
6726 				      else
6727 					mc->searched = mc->spare7 = 0;
6728 				  }
6729 			    }
6730 			    else
6731 			      return(peSelectError(interp, "malformed compound search"));
6732 			}
6733 			else
6734 			  return(peSelectError(interp, "malformed compound search"));
6735 		    }
6736 		  }
6737 		  else
6738 		    return(peSelectError(interp, "malformed compound search"));
6739 	    }
6740 	    else
6741 	      return(peSelectError(interp, "cmd cmdargs"));
6742 
6743 	    /*
6744 	     * at this point all interesting messages should
6745 	     * have searched bit lit
6746 	     */
6747 
6748 	    if(narrow)				/* make sure something was selected */
6749 	      for(i = 1L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
6750 		if(mail_elt(wps_global->mail_stream,
6751 			    mn_m2raw(sp_msgmap(wps_global->mail_stream), i))->searched){
6752 		    if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag))
6753 		      break;
6754 		    else
6755 		      mm_search_count--;
6756 		}
6757 
6758 	    diff = 0L;
6759 	    if(mm_search_count){
6760 		/*
6761 		 * loop thru all the messages, adjusting local flag bits
6762 		 * based on their "searched" bit...
6763 		 */
6764 		for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
6765 		  if(narrow){
6766 		      /* turning OFF selectedness if the "searched" bit isn't lit. */
6767 		      if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
6768 			  if(!mail_elt(wps_global->mail_stream,
6769 				       mn_m2raw(sp_msgmap(wps_global->mail_stream), i))->searched){
6770 			      diff--;
6771 			      set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag, 0);
6772 			      if(hidden)
6773 				set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, MN_HIDE, 1);
6774 			  }
6775 			  else if(msgno < mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6776 			    msgno = i;
6777 		      }
6778 		  }
6779 		  else if(mail_elt(wps_global->mail_stream,mn_m2raw(sp_msgmap(wps_global->mail_stream),i))->searched){
6780 		      /* turn ON selectedness if "searched" bit is lit. */
6781 		      if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
6782 			  diff++;
6783 			  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag, 1);
6784 			  if(hidden)
6785 			    set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, MN_HIDE, 0);
6786 		      }
6787 		  }
6788 
6789 		/* if we're zoomed and the current message was unselected */
6790 		if(narrow && msgno
6791 		   && get_lflag(wps_global->mail_stream,sp_msgmap(wps_global->mail_stream),mn_get_cur(sp_msgmap(wps_global->mail_stream)),MN_HIDE))
6792 		  mn_reset_cur(sp_msgmap(wps_global->mail_stream), msgno);
6793 	    }
6794 
6795 	    Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE);
6796 	}
6797 
6798 	return(TCL_OK);
6799     }
6800 
6801     Tcl_SetResult(interp, "Can't read select option", TCL_STATIC);
6802     return(TCL_ERROR);
6803 }
6804 
6805 int
peSelectNumber(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)6806 peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6807 {
6808     /*
6809      * Args: [broad | narrow] firstnumber lastnumber
6810      */
6811 
6812     long first = 0L, last = 0L, n;
6813 
6814     if(objc == 2){
6815 	if(Tcl_GetLongFromObj(interp, objv[0], &first) == TCL_OK
6816 	   && Tcl_GetLongFromObj(interp, objv[1], &last) == TCL_OK){
6817 	    if(last && last < first){
6818 		n = last;
6819 		last = first;
6820 		first = n;
6821 	    }
6822 
6823 	    if(first >= 1L && first <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
6824 		if(last){
6825 		    if(last >= 1L && last <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
6826 			for(n = first; n <= last; n++)
6827 			  mm_searched(wps_global->mail_stream,
6828 				      mn_m2raw(sp_msgmap(wps_global->mail_stream), n));
6829 		    }
6830 		    else
6831 		      return(peSelectError(interp, "last out of range"));
6832 		}
6833 		else{
6834 		    mm_searched(wps_global->mail_stream,
6835 				mn_m2raw(sp_msgmap(wps_global->mail_stream), first));
6836 		}
6837 	    }
6838 	    else
6839 	      return(peSelectError(interp, "first out of range"));
6840 	}
6841 	else
6842 	  return(peSelectError(interp, "can't read first/last"));
6843     }
6844     else
6845       return(peSelectError(interp, "num first last"));
6846 
6847     return(TCL_OK);
6848 }
6849 
6850 int
peSelectDate(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)6851 peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6852 {
6853     /*
6854      * Args: [broad | narrow]
6855      *	 tense - "on", "since", "before"
6856      *	 year - 4 digit year
6857      *	 month - abbreviated month "jan", "feb"...
6858      *	 day - day number
6859      */
6860 
6861     char *tense, *year, *month, *day, buf[256];
6862 
6863     if(objc == 4){
6864 	if((tense = peSelValTense(objv[0])) != NULL){
6865 	    if((year = peSelValYear(objv[1])) != NULL){
6866 		if((month = peSelValMonth(objv[2])) != NULL){
6867 		    if((day = peSelValDay(objv[3])) != NULL){
6868 			snprintf(buf, sizeof(buf), "%s %s-%s-%s", tense, day, month, year);
6869 			pine_mail_search_full(wps_global->mail_stream, NULL,
6870 					      mail_criteria(buf),
6871 					      SE_NOPREFETCH | SE_FREE);
6872 		    }
6873 		    else
6874 		      return(peSelectError(interp, "<with valid day>"));
6875 		}
6876 		else
6877 		  return(peSelectError(interp, "<with valid month>"));
6878 	    }
6879 	    else
6880 	      return(peSelectError(interp, "<with valid year>"));
6881 	}
6882 	else
6883 	  return(peSelectError(interp, "<with valid tense>"));
6884     }
6885     else
6886       return(peSelectError(interp, "date tense year monthabbrev daynum"));
6887 
6888     return(TCL_OK);
6889 }
6890 
6891 int
peSelectText(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)6892 peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6893 {
6894     /*
6895      * Args: [broad | narrow]
6896      *	 case - in not
6897      *	 field - to from cc recip partic subj any
6898      *	 text - free text search string
6899      */
6900     int  not;
6901     char field, *text;
6902 
6903     if(objc == 3){
6904 	if((not = peSelValCase(objv[0])) >= 0){
6905 	    if((field = peSelValField(objv[1])) != '\0'){
6906 		if((text = Tcl_GetStringFromObj(objv[2], NULL))
6907 		   && strlen(text) < 1024){
6908 		    /* BUG: fix charset not to be NULL below */
6909 		    if(agg_text_select(wps_global->mail_stream,
6910 				       sp_msgmap(wps_global->mail_stream),
6911 				       field, NULL, not, 0, text, NULL, NULL))
6912 		      /* BUG: plug in "charset" above? */
6913 		      return(peSelectError(interp, "programmer botch"));
6914 		}
6915 		else
6916 		  return(peSelectError(interp, "<with search string < 1024>"));
6917 	    }
6918 	    else
6919 	      return(peSelectError(interp, "<with valid field>"));
6920 	}
6921 	else
6922 	  return(peSelectError(interp, "<with valid case>"));
6923     }
6924     else
6925       return(peSelectError(interp, "text case field text"));
6926 
6927     return(TCL_OK);
6928 }
6929 
6930 int
peSelectStatus(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)6931 peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6932 {
6933     /*
6934      * Args: [broad | narrow]
6935      *	 case - on not
6936      *	 status - imp new ans del
6937      */
6938     int  not;
6939     char flag;
6940 
6941     if(objc == 2){
6942 	if((not = peSelValCase(objv[0])) >= 0){
6943 	    if((flag = peSelValFlag(objv[1])) != '\0'){
6944 		if(agg_flag_select(wps_global->mail_stream, not, flag, NULL))
6945 		  return(peSelectError(interp, "programmer botch"));
6946 	    }
6947 	    else
6948 	      return(peSelectError(interp, "<with valid flag>"));
6949 	}
6950 	else
6951 	  return(peSelectError(interp, "<with valid case>"));
6952     }
6953     else
6954       return(peSelectError(interp, "status focus case flag"));
6955 
6956     return(TCL_OK);
6957 }
6958 
6959 char *
peSelValTense(Tcl_Obj * objp)6960 peSelValTense(Tcl_Obj *objp)
6961 {
6962     char *tense, **pp;
6963 
6964     if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){
6965 	static char *tenses[] = {"on", "since", "before", NULL};
6966 
6967 	for(pp = tenses; *pp; pp++)
6968 	  if(!strucmp(*pp, tense))
6969 	    return(tense);
6970     }
6971 
6972     return(NULL);
6973 }
6974 
6975 
6976 char *
peSelValYear(Tcl_Obj * objp)6977 peSelValYear(Tcl_Obj *objp)
6978 {
6979     char *year;
6980 
6981     return((year = Tcl_GetStringFromObj(objp, NULL))
6982 	   && strlen(year) == 4
6983 	   && isdigit((unsigned char) year[0])
6984 	   && isdigit((unsigned char) year[0])
6985 	   && isdigit((unsigned char) year[0])
6986 	     ? year
6987 	     : NULL);
6988 }
6989 
6990 
6991 char *
peSelValMonth(Tcl_Obj * objp)6992 peSelValMonth(Tcl_Obj *objp)
6993 {
6994     char *month, **pp;
6995     static char *mons[] = {"jan","feb","mar","apr",
6996 			   "may","jun","jul","aug",
6997 			   "sep","oct","nov","dec", NULL};
6998 
6999     if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3)
7000       for(pp = mons; *pp; pp++)
7001 	if(!strucmp(month, *pp))
7002 	  return(*pp);
7003 
7004     return(NULL);
7005 }
7006 
7007 
7008 char *
peSelValDay(Tcl_Obj * objp)7009 peSelValDay(Tcl_Obj *objp)
7010 {
7011     char *day;
7012 
7013     return(((day = Tcl_GetStringFromObj(objp, NULL))
7014 	    && (day[0] == '0' || day[0] == '1'
7015 		|| day[0] == '2' || day[0] == '3')
7016 	    && isdigit((unsigned char) day[1])
7017 	    && day[2] == '\0')
7018 	       ? day
7019 	       : NULL);
7020 }
7021 
7022 
7023 int
peSelValCase(Tcl_Obj * objp)7024 peSelValCase(Tcl_Obj *objp)
7025 {
7026     char *not;
7027 
7028     if((not = Tcl_GetStringFromObj(objp, NULL)) != NULL){
7029 	if(!strucmp(not, "ton"))
7030 	  return(0);
7031 	else if(!strucmp(not, "not"))
7032 	  return(1);
7033     }
7034 
7035     return(-1);
7036 }
7037 
7038 
7039 int
peSelValField(Tcl_Obj * objp)7040 peSelValField(Tcl_Obj *objp)
7041 {
7042     char *field;
7043     int   i;
7044     static struct {
7045 	char *field;
7046 	int   type;
7047     } fields[] = {{"from",   'f'},
7048 		  {"to",     't'},
7049 		  {"cc",     'c'},
7050 		  {"subj",   's'},
7051 		  {"any",    'a'},
7052 		  {"recip",  'r'},
7053 		  {"partic", 'p'},
7054 		  {"body",   'b'},
7055 		  {NULL,0}};
7056 
7057     if((field = Tcl_GetStringFromObj(objp, NULL)) != NULL)
7058       for(i = 0; fields[i].field ; i++)
7059 	if(!strucmp(fields[i].field, field))
7060 	  return(fields[i].type);
7061 
7062     return(0);
7063 }
7064 
7065 
7066 int
peSelValFlag(Tcl_Obj * objp)7067 peSelValFlag(Tcl_Obj *objp)
7068 {
7069     char *flag;
7070     int   i;
7071     static struct {
7072 	char *flag;
7073 	int   type;
7074     } flags[] = {{"imp",   '*'},
7075 		 {"new",   'n'},
7076 		 {"ans",   'a'},
7077 		 {"del",   'd'},
7078 		 {NULL,0}};
7079 
7080     if((flag = Tcl_GetStringFromObj(objp, NULL)) != NULL)
7081       for(i = 0; flags[i].flag ; i++)
7082 	if(!strucmp(flags[i].flag, flag))
7083 	  return(flags[i].type);
7084 
7085     return(0);
7086 }
7087 
7088 int
peSelected(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int matchflag)7089 peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
7090 {
7091     int   rv = 0;
7092     long  i, n;
7093     char *range;
7094 
7095     /*
7096      * CMD: searched [before | after] #
7097      *
7098      * Returns: 1 if criteria is true, 0 otherwise
7099      */
7100 
7101     if((range = Tcl_GetStringFromObj(objv[0], NULL))
7102        && Tcl_GetLongFromObj(interp, objv[1], &n) != TCL_ERROR){
7103 	if(!strucmp(range, "before")){
7104 	    for(i = 1L; i < n && i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
7105 	      if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
7106 		  rv = 1;
7107 		  break;
7108 	      }
7109 
7110 	    Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7111 	    return(TCL_OK);
7112 	}
7113 	else if(!strucmp(range, "after")){
7114 	    for(i = n + 1L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
7115 	      if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
7116 		  rv = 1;
7117 		  break;
7118 	      }
7119 
7120 	    Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7121 	    return(TCL_OK);
7122 	}
7123     }
7124 
7125     Tcl_SetResult(interp, "searched test failed", TCL_STATIC);
7126     return(TCL_ERROR);
7127 }
7128 
7129 
7130 int
peSelectError(Tcl_Interp * interp,char * usage)7131 peSelectError(Tcl_Interp *interp, char *usage)
7132 {
7133     char buf[256];
7134 
7135     snprintf(buf, sizeof(buf), "should be select %.128s", usage);
7136     Tcl_SetResult(interp, buf, TCL_VOLATILE);
7137     return(TCL_ERROR);
7138 }
7139 
7140 
7141 int
peApply(Tcl_Interp * interp,int objc,Tcl_Obj ** objv)7142 peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv)
7143 {
7144     char *subcmd;
7145     long  n;
7146 
7147     if(!(n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT))){
7148 	Tcl_SetResult(interp, "No messages selected", TCL_STATIC);
7149 	return(TCL_ERROR);
7150     }
7151     else if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
7152 	if(objc == 1){
7153 	    if(!strucmp(subcmd, "delete")){
7154 		/* BUG: is CmdWhere arg always right? */
7155 		(void) cmd_delete(wps_global, sp_msgmap(wps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL);
7156 		Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7157 		return(TCL_OK);
7158 	    }
7159 	    else if(!strucmp(subcmd, "undelete")){
7160 		(void) cmd_undelete(wps_global, sp_msgmap(wps_global->mail_stream), MCMD_AGG | MCMD_SILENT);
7161 		Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7162 		return(TCL_OK);
7163 	    }
7164 	}
7165 	else if(objc == 2){
7166 	    if(!strucmp(subcmd, "count")){
7167 		/*
7168 		 * Args: flag
7169 		 */
7170 		char *flagname;
7171 		long  n, rawno, count = 0;
7172 
7173 		if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
7174 		    for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++){
7175 			rawno = mn_m2raw(sp_msgmap(wps_global->mail_stream), n);
7176 			if(get_lflag(wps_global->mail_stream, NULL, rawno, MN_SLCT)
7177 			   && peIsFlagged(wps_global->mail_stream,
7178 					  mail_uid(wps_global->mail_stream, rawno),
7179 					  flagname)){
7180 			    count++;
7181 			}
7182 		    }
7183 		}
7184 
7185 		Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
7186 		return(TCL_OK);
7187 	    }
7188 	}
7189 	else if(objc == 3){
7190 	    if(!strucmp(subcmd, "flag")){
7191 		/*
7192 		 * Args: case - on not
7193 		 *	 flag - imp new ans del
7194 		 */
7195 		char flag, *result;
7196 		int  not;
7197 		long flagged;
7198 
7199 		if((not = peSelValCase(objv[1])) >= 0){
7200 		    if((flag = peSelValFlag(objv[2])) != '\0'){
7201 			result = peApplyFlag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), flag, not, &flagged);
7202 			if(!result){
7203 			    Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE);
7204 			    return(TCL_OK);
7205 			}
7206 			else
7207 			  return(peApplyError(interp, result));
7208 		    }
7209 		    else
7210 		      return(peApplyError(interp, "invalid flag"));
7211 		}
7212 		else
7213 		  return(peApplyError(interp, "invalid case"));
7214 	    }
7215 	    else if(!strucmp(subcmd, "save")){
7216 		/*
7217 		 * Args: colid -
7218 		 *	 folder - imp new ans del
7219 		 */
7220 
7221 		int	   colid, flgs = 0, i;
7222 		char	  *folder, *err;
7223 		CONTEXT_S *cp;
7224 
7225 		if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7226 
7227 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7228 		      if(i == colid){
7229 			  if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7230 			      if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7231 
7232 				  if(!READONLY_FOLDER(wps_global->mail_stream)
7233 				     && F_OFF(F_SAVE_WONT_DELETE, wps_global))
7234 				    flgs |= SV_DELETE;
7235 
7236 				  if(colid == 0 && !strucmp(folder, "inbox"))
7237 				    flgs |= SV_INBOXWOCNTXT;
7238 
7239 				  i = save(wps_global, wps_global->mail_stream,
7240 					   cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7241 
7242 				  err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem saving";
7243 
7244 				  restore_selected(sp_msgmap(wps_global->mail_stream));
7245 				  if(err)
7246 				    return(peApplyError(interp, err));
7247 
7248 				  Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7249 				  return(TCL_OK);
7250 			      }
7251 			      else
7252 				return(peApplyError(interp, "can't select"));
7253 			  }
7254 			  else
7255 			    return(peApplyError(interp, "no folder name"));
7256 		      }
7257 
7258 		      return(peApplyError(interp, "bad colid"));
7259 		}
7260 		else
7261 		  return(peApplyError(interp, "invalid case"));
7262 	    }
7263 	    else if(!strucmp(subcmd, "copy")){
7264 		/*
7265 		 * Args: colid -
7266 		 *	 folder - imp new ans del
7267 		 */
7268 
7269 		int	   colid, flgs = 0, i;
7270 		char	  *folder, *err;
7271 		CONTEXT_S *cp;
7272 
7273 		if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7274 
7275 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7276 		      if(i == colid){
7277 			  if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7278 			      if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7279 
7280 				  if(colid == 0 && !strucmp(folder, "inbox"))
7281 				    flgs |= SV_INBOXWOCNTXT;
7282 
7283 				  i = save(wps_global, wps_global->mail_stream,
7284 					   cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7285 
7286 				  err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem copying";
7287 
7288 				  restore_selected(sp_msgmap(wps_global->mail_stream));
7289 				  if(err)
7290 				    return(peApplyError(interp, err));
7291 
7292 				  Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7293 				  return(TCL_OK);
7294 			      }
7295 			      else
7296 				return(peApplyError(interp, "can't select"));
7297 			  }
7298 			  else
7299 			    return(peApplyError(interp, "no folder name"));
7300 		      }
7301 
7302 		      return(peApplyError(interp, "bad colid"));
7303 		}
7304 		else
7305 		  return(peApplyError(interp, "invalid case"));
7306 	    }
7307 	    else if(!strucmp(subcmd, "move")){
7308 		/*
7309 		 * Args: colid -
7310 		 *	 folder - imp new ans del
7311 		 */
7312 
7313 		int	   colid, flgs = 0, i;
7314 		char	  *folder, *err;
7315 		CONTEXT_S *cp;
7316 
7317 		if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7318 
7319 		    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7320 		      if(i == colid){
7321 			  if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7322 			      if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7323 
7324 				  flgs = SV_DELETE;
7325 
7326 				  if(colid == 0 && !strucmp(folder, "inbox"))
7327 				    flgs |= SV_INBOXWOCNTXT;
7328 
7329 				  i = save(wps_global, wps_global->mail_stream,
7330 					   cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7331 
7332 				  err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem moving";
7333 
7334 				  restore_selected(sp_msgmap(wps_global->mail_stream));
7335 				  if(err)
7336 				    return(peApplyError(interp, err));
7337 
7338 				  Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7339 				  return(TCL_OK);
7340 			      }
7341 			      else
7342 				return(peApplyError(interp, "can't select"));
7343 			  }
7344 			  else
7345 			    return(peApplyError(interp, "no folder name"));
7346 		      }
7347 
7348 		      return(peApplyError(interp, "bad colid"));
7349 		}
7350 		else
7351 		  return(peApplyError(interp, "invalid case"));
7352 	    }
7353 	    else if(!strucmp(subcmd, "spam")){
7354 		/*
7355 		 * Args: spamaddr -
7356 		 *	 spamsubj -
7357 		 */
7358 		char *spamaddr, *spamsubj = NULL;
7359 		long  n, rawno;
7360 
7361 		if((spamaddr = Tcl_GetStringFromObj(objv[1], NULL))
7362 		   && (spamsubj = Tcl_GetStringFromObj(objv[2], NULL))){
7363 		    for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++){
7364 			rawno = mn_m2raw(sp_msgmap(wps_global->mail_stream), n);
7365 			if(get_lflag(wps_global->mail_stream, NULL, rawno, MN_SLCT)){
7366 			    char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
7367 
7368 			    if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){
7369 				Tcl_SetResult(interp, rs, TCL_VOLATILE);
7370 				return(TCL_ERROR);
7371 			    }
7372 			}
7373 		    }
7374 		}
7375 
7376 		Tcl_SetResult(interp, "OK", TCL_VOLATILE);
7377 		return(TCL_OK);
7378 	    }
7379 	}
7380     }
7381 
7382     return(peApplyError(interp, "unknown option"));
7383 }
7384 
7385 
7386 char *
peApplyFlag(MAILSTREAM * stream,MSGNO_S * msgmap,char flag,int not,long * flagged)7387 peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged)
7388 {
7389     char *seq, *flagstr;
7390     long  flags, flagid;
7391 
7392     switch (flag) {
7393       case '*' :
7394 	flagstr = "\\FLAGGED";
7395 	flags = not ? 0L : ST_SET;
7396 	flagid = not ? F_FLAG : F_UNFLAG;
7397 	break;
7398       case 'n' :
7399 	flagstr = "\\SEEN";
7400 	flags = not ? ST_SET : 0L;
7401 	flagid = not ? F_UNSEEN : F_SEEN;
7402 	break;
7403       case 'a' :
7404 	flagstr = "\\ANSWERED";
7405 	flags = not ? 0L : ST_SET;
7406 	flagid = not ? F_ANS : F_UNANS;
7407 	break;
7408       case 'd':
7409 	flagstr = "\\DELETED";
7410 	flags = not ? 0L : ST_SET;
7411 	flagid = not ? F_DEL : F_UNDEL;
7412 	break;
7413       default :
7414 	return("unknown flag");
7415 	break;
7416     }
7417 
7418     if(pseudo_selected(stream, msgmap)){
7419 	if((seq = currentf_sequence(stream, msgmap, flagid, flagged, 1, NULL, NULL)) != NULL){
7420 	    mail_flag(stream, seq, flagstr, flags);
7421 	    fs_give((void **) &seq);
7422 	}
7423 
7424 	restore_selected(msgmap);
7425 	return(NULL);
7426     }
7427     else
7428       return("can't select");
7429 }
7430 
7431 
7432 int
peApplyError(Tcl_Interp * interp,char * usage)7433 peApplyError(Tcl_Interp *interp, char *usage)
7434 {
7435     char buf[256];
7436 
7437     snprintf(buf, sizeof(buf), "apply error: %.128s", usage);
7438     Tcl_SetResult(interp, buf, TCL_VOLATILE);
7439     return(TCL_ERROR);
7440 }
7441 
7442 
7443 /*
7444  * peIndexFormat - Return with interp's result object set to
7445  *		   represent the index line's format as a list of
7446  *		   index-field-name, percentage-width pairs
7447  */
7448 int
peIndexFormat(Tcl_Interp * interp)7449 peIndexFormat(Tcl_Interp *interp)
7450 {
7451     INDEX_COL_S	    *cdesc = NULL;
7452     char	    *name, wbuf[4], *dname;
7453 
7454     for(cdesc = wps_global->index_disp_format;
7455 	cdesc->ctype != iNothing;
7456 	cdesc++) {
7457 	dname = NULL;
7458 	switch(cdesc->ctype){
7459 	  case iFStatus:
7460 	  case iIStatus:
7461 	  case iSIStatus:
7462 	    dname = "iStatus";
7463 	  case iStatus:
7464 	    name = "Status";
7465 	    break;
7466 
7467 	  case iMessNo:
7468 	    name = "Number";
7469 	    break;
7470 
7471 	  case iPrio:
7472 	  case iPrioAlpha:
7473 	  case iPrioBang:
7474 	    name = "Priority";
7475 	    break;
7476 
7477 	  case iDate:     case iSDate:      case iSTime:     case iLDate:
7478 	  case iS1Date:   case iS2Date:     case iS3Date:    case iS4Date:    case iDateIso:
7479 	  case iDateIsoS:
7480 	  case iSDateIso: case iSDateIsoS:
7481 	  case iSDateS1:  case iSDateS2:
7482 	  case iSDateS3:  case iSDateS4:
7483 	  case iSDateTime:
7484 	  case iSDateTimeIso:               case iSDateTimeIsoS:
7485 	  case iSDateTimeS1:                case iSDateTimeS2:
7486 	  case iSDateTimeS3:                case iSDateTimeS4:
7487 	  case iSDateTime24:
7488 	  case iSDateTimeIso24:             case iSDateTimeIsoS24:
7489 	  case iSDateTimeS124:              case iSDateTimeS224:
7490 	  case iSDateTimeS324:              case iSDateTimeS424:
7491 	  case iCurDate:  case iCurDateIso: case iCurDateIsoS:
7492 	  case iCurTime24:		    case iCurTime12:
7493 	  case iCurPrefDate:
7494 	    name = "Date";
7495 	    break;
7496 
7497 	  case iCurDay:			    case iCurDay2Digit:
7498 	  case iCurDayOfWeek:		    case iCurDayOfWeekAbb:
7499 	    name = "Day";
7500 	    break;
7501 
7502 	  case iCurMon:			    case iCurMon2Digit:
7503 	  case iCurMonLong:		    case iCurMonAbb:
7504 	    name= "Month";
7505 	    break;
7506 
7507 	  case iTime24:     case iTime12:    case iTimezone:
7508 	  case iCurPrefTime:
7509 	    name = "Time";
7510 	    break;
7511 
7512 	  case iDay2Digit:  case iDayOfWeek:  case iDayOfWeekAbb:
7513 	    name = "Day";
7514 	    break;
7515 
7516 	  case iMonAbb: case iMon2Digit:
7517 	    name = "Month";
7518 	    break;
7519 
7520 	  case iYear: case iYear2Digit:
7521 	  case iCurYear: case iCurYear2Digit:
7522 	    name = "Year";
7523 	    break;
7524 
7525 	  case iScore :
7526 	    name = "Score";
7527 	    break;
7528 
7529 	  case iFromTo:
7530 	  case iFromToNotNews:
7531 	  case iFrom:
7532 	    name = "From";
7533 	    break;
7534 
7535 	  case iTo:
7536 	  case iToAndNews :
7537 	    name = "To";
7538 	    break;
7539 
7540 	  case iCc:
7541 	    name = "Cc";
7542 	    break;
7543 
7544 	  case iRecips:
7545 	    name = "Recipients";
7546 	    break;
7547 
7548 	  case iSender:
7549 	    name = "Sender";
7550 	    break;
7551 
7552 	  case iSize :
7553 	  case iSizeComma :
7554 	  case iSizeNarrow :
7555 	  case iDescripSize:
7556 	  case iKSize :
7557 	    name = "Size";
7558 	    break;
7559 
7560 	  case iAtt:
7561 	    name = "Attachments";
7562 	    break;
7563 
7564 	  case iAddress :
7565 	    name = "Address";
7566 	    break;
7567 
7568 	  case iMailbox :
7569 	    name = "Mailbox";
7570 	    break;
7571 
7572 	  case iOpeningText:
7573 	  case iOpeningTextNQ:
7574 	    name = "Excerpt";
7575 	    break;
7576 
7577 	  case iSubject :
7578 	  case iSubjKey :
7579 	  case iSubjKeyInit :
7580 	  case iSubjectText :
7581 	  case iSubjKeyText :
7582 	  case iShortSubject :
7583 	  case iShortSubjKey :
7584 	  case iSubjKeyInitText :
7585 	  case iShortSubjKeyInit :
7586 	    name = "Subject";
7587 	    break;
7588 
7589 	  case iNews:
7590 	  case iNewsAndTo :
7591 	    name = "News";
7592 	    break;
7593 
7594 	  case iNewsAndRecips:
7595 	    name = "News/Recip";
7596 	    break;
7597 
7598 	  case iRecipsAndNews:
7599 	    name = "Recip/News";
7600 	    break;
7601 
7602 	  default :
7603 	    name = "";
7604 	    break;
7605 	}
7606 
7607 	if(cdesc->width > 0){
7608 	    int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH);
7609 
7610 	    snprintf(wbuf, sizeof(wbuf), "%d%%", p);
7611 	}
7612 	else
7613 	  wbuf[0] = '\0';
7614 
7615 	if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK)
7616 	  return(TCL_ERROR);
7617     }
7618 
7619     return(TCL_OK);
7620 }
7621 
7622 
7623 int
peNewMailResult(Tcl_Interp * interp)7624 peNewMailResult(Tcl_Interp *interp)
7625 {
7626     unsigned long n, uid;
7627 
7628     if(sp_mail_box_changed(wps_global->mail_stream)){
7629 	if((n = sp_mail_since_cmd(wps_global->mail_stream)) != 0L){
7630 	    /* first element is count of new messages */
7631 	    if(Tcl_ListObjAppendElement(interp,
7632 					Tcl_GetObjResult(interp),
7633 					Tcl_NewLongObj(n)) != TCL_OK)
7634 	      return(TCL_ERROR);
7635 
7636 	    /* second element is UID of most recent message */
7637 	    for(uid = wps_global->mail_stream->nmsgs; uid > 1L; uid--)
7638 	      if(!get_lflag(wps_global->mail_stream, NULL, uid, MN_EXLD))
7639 		break;
7640 
7641 	    if(!uid){
7642 		Tcl_ResetResult(interp);
7643 		Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7644 		return(TCL_ERROR);
7645 	    }
7646 
7647 	    uid = mail_uid(wps_global->mail_stream, uid);
7648 
7649 	    if(Tcl_ListObjAppendElement(interp,
7650 					Tcl_GetObjResult(interp),
7651 					Tcl_NewLongObj(uid)) != TCL_OK)
7652 	      return(TCL_ERROR);
7653 	}
7654 	else {
7655 	    if(Tcl_ListObjAppendElement(interp,
7656 					Tcl_GetObjResult(interp),
7657 					Tcl_NewIntObj(0)) != TCL_OK)
7658 	      return(TCL_ERROR);
7659 
7660 	    /* zero is UID of new message */
7661 	    if(Tcl_ListObjAppendElement(interp,
7662 					Tcl_GetObjResult(interp),
7663 					Tcl_NewIntObj(0)) != TCL_OK)
7664 	      return(TCL_ERROR);
7665 	}
7666 
7667 	/* third element is expunge count */
7668 	if(Tcl_ListObjAppendElement(interp,
7669 				    Tcl_GetObjResult(interp),
7670 				    Tcl_NewLongObj(sp_expunge_count(wps_global->mail_stream)
7671 						   ? sp_expunge_count(wps_global->mail_stream)
7672 						   : 0L)) != TCL_OK)
7673 	  return(TCL_ERROR);
7674 
7675     }
7676     else
7677       Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7678 
7679     return(TCL_OK);
7680 }
7681 
7682 
7683 /* * * * * * * *  Start of Per-Thread/SubThread access functions * * * * * * * */
7684 
7685 
7686 /*
7687  * PEThreadCmd - access/manipulate various pieces of thread state
7688  */
7689 int
PEThreadCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7690 PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7691 {
7692     char *err, errbuf[256], *cmd, *op;
7693     long  uidl;
7694     imapuid_t  uid;
7695 
7696     dprint((2, "PEThreadCmd"));
7697 
7698     snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7699 	    Tcl_GetStringFromObj(objv[0], NULL));
7700 
7701     if(!(wps_global && wps_global->mail_stream)){
7702 	snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7703 		Tcl_GetStringFromObj(objv[0], NULL));
7704     }
7705     else if(objc < 2){
7706 	Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?");
7707     }
7708     else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7709 	return(TCL_ERROR); /* conversion problem? */
7710     }
7711     else if(!peSequenceNumber(uidl)){
7712 	snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7713 		Tcl_GetStringFromObj(objv[0], NULL), uidl);
7714     }
7715     else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7716 	uid = uidl;
7717 	if(objc == 3){
7718 	    if(!strucmp(cmd,"info")){
7719 #define	WP_MAX_THRD_PREFIX 256
7720 		long raw;
7721 		PINETHRD_S *pthrd;
7722 		char tstr[WP_MAX_THRD_PREFIX];
7723 
7724 		if((raw = peSequenceNumber(uid)) != 0L){
7725 		    /*
7726 		     * translate PINETHRD_S data into
7727 		     */
7728 		    if((pthrd = msgno_thread_info(wps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){
7729 
7730 			tstr[0] = '\0';
7731 /* BUG: build tstr form pthrd */
7732 
7733 
7734 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7735 						 Tcl_NewStringObj(tstr, -1));
7736 		    }
7737 		}
7738 		else
7739 		  Tcl_SetResult(interp, "0", TCL_STATIC);
7740 
7741 		return(TCL_OK);
7742 	    }
7743 
7744 	}
7745 	else if(objc == 5){
7746 	    if(!strucmp(cmd,"flag")){
7747 		if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
7748 		    if(!strucmp(op,"deleted")){
7749 			int value;
7750 
7751 			if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){
7752 			    long n;
7753 			    PINETHRD_S *pthrd;
7754 			    char *flag;
7755 
7756 			    while(1){
7757 				if(!(n = peSequenceNumber(uid))){
7758 				    Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC);
7759 				    return(TCL_ERROR);
7760 				}
7761 
7762 				flag = cpystr("\\DELETED");
7763 				mail_flag(wps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L));
7764 				fs_give((void **) &flag);
7765 
7766 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7767 							 Tcl_NewStringObj(ulong2string(uid), -1));
7768 
7769 				if(++n <= wps_global->mail_stream->nmsgs){
7770 				    uid = mail_uid(wps_global->mail_stream, n);
7771 				}
7772 				else
7773 				  break;
7774 
7775 				if((pthrd = msgno_thread_info(wps_global->mail_stream, n, NULL,THD_TOP)) != NULL){
7776 				}
7777 				else
7778 				  break;
7779 			    }
7780 			}
7781 		    }
7782 		}
7783 	    }
7784 	}
7785     }
7786 
7787     Tcl_SetResult(interp, err, TCL_STATIC);
7788     return(TCL_ERROR);
7789 }
7790 
7791 
7792 
7793 /* * * * * * * *  Start of Per-Message access functions * * * * * * * */
7794 
7795 
7796 
7797 static struct _message_cmds {
7798     char	*cmd;
7799     int		 hcount;
7800     struct {
7801 	int	 argcount;
7802 	int    (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
7803     } h[3];
7804 } message_cmds[] = {
7805     {"size",		1,  {{3, peMessageSize}}},
7806     {"date",		2,  {{3, peMessageDate}, {4, peMessageDate}}},
7807     {"subject",		1,  {{3, peMessageSubject}}},
7808     {"fromaddr",	1,  {{3, peMessageFromAddr}}},
7809     {"toaddr",		1,  {{3, peMessageToAddr}}},
7810     {"ccaddr",		1,  {{3, peMessageCcAddr}}},
7811     {"status",		1,  {{3, peMessageStatus}}},
7812     {"statusbits",	1,  {{3, peMessageStatusBits}}},
7813     {"charset",		1,  {{3, peMessageCharset}}},
7814     {"number",		1,  {{3, peMsgnoFromUID}}},
7815     {"envelope",	0},
7816     {"rawenvelope",	0},
7817     {"text",		1,  {{3, peMessageText}}},
7818     {"header",		1,  {{3, peMessageHeader}}},
7819     {"attachments",	1,  {{3, peMessageAttachments}}},
7820     {"body",		3,  {{3, peMessageBody}, {4, peMessageBody}}},
7821     {"cid",		1,  {{4, peMessagePartFromCID}}},
7822     {"flag",		2,  {{4, peGetFlag}, {5, peSetFlag}}},
7823     {"replyheaders",	2,  {{3, peReplyHeaders},{4, peReplyHeaders}}},
7824     {"replytext",	2,  {{4, peReplyText}, {5, peReplyText}}},
7825     {"forwardheaders",	2,  {{3, peForwardHeaders}, {4, peForwardHeaders}}},
7826     {"forwardtext",	2,  {{3, peForwardText}, {4, peForwardText}}},
7827     {"rawbody",		0},
7828     {"select",		2,  {{3, peMsgSelect}, {4, peMsgSelect}}},
7829     {"detach",		1,  {{5, peDetach}}},
7830     {"attachinfo",	1,  {{4, peAttachInfo}}},
7831     {"savedefault",	1,  {{3, peSaveDefault}}},
7832     {"save",		1,  {{5, peSave}}},
7833     {"copy",		1,  {{5, peCopy}}},
7834     {"move",		1,  {{5, peMove}}},
7835     {"takeaddr",       	1,  {{3, peTakeaddr}}},
7836     {"takefrom",       	1,  {{3, peTakeFrom}}},
7837     {"replyquote",	1,  {{3, peReplyQuote}}},
7838     {"bounce",		2,  {{4, peMessageBounce},{5, peMessageBounce}}},
7839     {"spam",		1,  {{5, peMessageSpamNotice}}},
7840     {"needpasswd",	1,  {{3, peMessageNeedPassphrase}}},
7841     {NULL, 0}
7842 };
7843 
7844 
7845 
7846 
7847 /*
7848  * PEMessageCmd - export various bits of message information
7849  *
7850  *   NOTE: all exported commands are of the form:
7851  *
7852  *             PEMessage <uid> <cmd> <args>
7853  */
7854 int
PEMessageCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7855 PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7856 {
7857     char *err, errbuf[256], *cmd;
7858     int   i, j;
7859     long uidl;
7860     imapuid_t  uid;
7861 
7862     dprint((5, "PEMessageCmd"));
7863 
7864     snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7865 	    Tcl_GetStringFromObj(objv[0], NULL));
7866 
7867     if(!(wps_global && wps_global->mail_stream)){
7868 	snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7869 		Tcl_GetStringFromObj(objv[0], NULL));
7870     }
7871     else if(objc < 3){
7872 	Tcl_WrongNumArgs(interp, 0, objv, "PEMessage <uid> cmd ?args?");
7873     }
7874     else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7875 	return(TCL_ERROR); /* conversion problem? */
7876     }
7877     else if(!peMessageNumber(uidl)){
7878 	snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7879 		Tcl_GetStringFromObj(objv[0], NULL), uidl);
7880     }
7881     else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7882 	uid = uidl;
7883 	for(i = 0; message_cmds[i].cmd; i++)
7884 	  if(!strcmp(cmd, message_cmds[i].cmd)){
7885 	      for(j = 0; j < message_cmds[i].hcount; j++)
7886 		if(message_cmds[i].h[j].argcount == objc)
7887 		  return((*message_cmds[i].h[j].f)(interp, uid, objc - 3,
7888 						   &((Tcl_Obj **)objv)[3]));
7889 
7890 	      snprintf(err = errbuf, sizeof(errbuf),
7891 		       "PEMessage: %s: mismatched argument count", cmd);
7892 	      break;
7893 	  }
7894     }
7895 
7896     Tcl_SetResult(interp, err, TCL_STATIC);
7897     return(TCL_ERROR);
7898 }
7899 
7900 
7901 /*
7902  * return the uid's ordinal number within the CURRENT SORT
7903  */
7904 long
peMessageNumber(imapuid_t uid)7905 peMessageNumber(imapuid_t uid)
7906 {
7907     return(mn_raw2m(sp_msgmap(wps_global->mail_stream), peSequenceNumber(uid)));
7908 }
7909 
7910 /*
7911  * return the uid's RAW message number (for c-client reference, primarily)
7912  */
7913 long
peSequenceNumber(imapuid_t uid)7914 peSequenceNumber(imapuid_t uid)
7915 {
7916     return(mail_msgno(wps_global->mail_stream, uid));
7917 }
7918 
7919 
7920 int
peMessageSize(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)7921 peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7922 {
7923     long raw;
7924 
7925     if((raw = peSequenceNumber(uid))
7926        && pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL)){
7927 	Tcl_SetResult(interp,
7928 		      long2string(mail_elt(wps_global->mail_stream,
7929 					   raw)->rfc822_size),
7930 		      TCL_VOLATILE);
7931     }
7932     else
7933       Tcl_SetResult(interp, "0", TCL_STATIC);
7934 
7935     return(TCL_OK);
7936 }
7937 
7938 
7939 int
peMessageDate(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)7940 peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7941 {
7942     char	 *cmd;
7943     long	 raw;
7944     ENVELOPE	*env;
7945     MESSAGECACHE mc;
7946 
7947     if((raw = peSequenceNumber(uid))
7948        && (env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL))){
7949 	if(objc == 1 && objv[0]){
7950 	    if(mail_parse_date(&mc, env->date)){
7951 		if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
7952 		    if(!strucmp(cmd,"day")){
7953 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day);
7954 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
7955 			return(TCL_OK);
7956 		    }
7957 		    else if(!strucmp(cmd,"month")){
7958 			Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE);
7959 			return(TCL_OK);
7960 		    }
7961 		    else if(!strucmp(cmd,"year")){
7962 			Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE);
7963 			return(TCL_OK);
7964 		    }
7965 		    else{
7966 			snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd);
7967 			Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
7968 		    }
7969 		}
7970 		else
7971 		  Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC);
7972 	    }
7973 	    else
7974 	      Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC);
7975 	}
7976 	else{
7977 	    Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE);
7978 	    return(TCL_OK);
7979 	}
7980     }
7981     else
7982       Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC);
7983 
7984     return(TCL_ERROR);
7985 }
7986 
7987 
7988 int
peMessageFromAddr(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)7989 peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7990 {
7991     return(peMessageField(interp, uid, "from"));
7992 }
7993 
7994 
7995 int
peMessageToAddr(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)7996 peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7997 {
7998     return(peMessageField(interp, uid, "to"));
7999 }
8000 
8001 
8002 int
peMessageCcAddr(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8003 peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8004 {
8005     return(peMessageField(interp, uid, "cc"));
8006 }
8007 
8008 
8009 int
peMessageSubject(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8010 peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8011 {
8012     return(peMessageField(interp, uid, "subject"));
8013 }
8014 
8015 
8016 int
peMessageField(Tcl_Interp * interp,imapuid_t uid,char * field)8017 peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field)
8018 {
8019     long      raw;
8020     char     *s = "";
8021     ENVELOPE *env;
8022 
8023     if((raw = peSequenceNumber(uid))
8024        && (env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL))){
8025 	if(!strucmp(field, "from")){
8026 	    if(env->from && env->from->mailbox)
8027 	      snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox,
8028 		      (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : "");
8029 	}
8030 	else if(!strucmp(field, "to")){
8031 	    if(env->to && env->to->mailbox)
8032 	      snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox,
8033 		      (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : "");
8034 	}
8035 	else if(!strucmp(field, "cc")){
8036 	    if(env->cc && env->cc->mailbox)
8037 	      snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox,
8038 		      (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : "");
8039 	}
8040 	else if(!strucmp(field, "subject")){
8041 	    if(env->subject)
8042 	      snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject);
8043 	}
8044 	else{
8045 	    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field);
8046 	    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
8047 	    return(TCL_ERROR);
8048 	}
8049 
8050 	Tcl_SetResult(interp, s, TCL_VOLATILE);
8051 	return(TCL_OK);
8052     }
8053 
8054     Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC);
8055     return(TCL_ERROR);
8056 }
8057 
8058 
8059 int
peMessageStatus(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8060 peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8061 {
8062     long	  raw;
8063     MESSAGECACHE *mc;
8064 
8065     if((raw = peSequenceNumber(uid)) != 0L){
8066 	if(!((mc = mail_elt(wps_global->mail_stream, raw))
8067 	     && mc->valid)){
8068 	    mail_fetch_flags(wps_global->mail_stream,
8069 			     ulong2string(uid), FT_UID);
8070 	    mc = mail_elt(wps_global->mail_stream, raw);
8071 	}
8072 
8073 	if (mc->deleted)
8074 	  Tcl_ListObjAppendElement(interp,
8075 				   Tcl_GetObjResult(interp),
8076 				   Tcl_NewStringObj("Deleted", -1));
8077 
8078 	if (mc->answered)
8079 	  Tcl_ListObjAppendElement(interp,
8080 				   Tcl_GetObjResult(interp),
8081 				   Tcl_NewStringObj("Answered", -1));
8082 
8083 	if (!mc->seen)
8084 	  Tcl_ListObjAppendElement(interp,
8085 				   Tcl_GetObjResult(interp),
8086 				   Tcl_NewStringObj("New", -1));
8087 
8088 	if (mc->flagged)
8089 	  Tcl_ListObjAppendElement(interp,
8090 				   Tcl_GetObjResult(interp),
8091 				   Tcl_NewStringObj("Important", -1));
8092     }
8093 
8094     return(TCL_OK);
8095 }
8096 
8097 
8098 int
peMessageCharset(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8099 peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8100 {
8101     /* everything coming out of pith better be utf-8 */
8102     Tcl_SetResult(interp, "UTF-8", TCL_STATIC);
8103     return(TCL_OK);
8104 }
8105 
8106 
8107 int
peMessageNeedPassphrase(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8108 peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8109 {
8110 #ifdef SMIME
8111     return((wps_global && wps_global->smime && wps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR);
8112 #else
8113     return(TCL_ERROR);
8114 #endif /* SMIME */
8115 }
8116 
8117 
8118 int
peMsgnoFromUID(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8119 peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8120 {
8121     Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE);
8122     return(TCL_OK);
8123 }
8124 
8125 
8126 /*
8127  * peInterpWritec - collect filtered output, appending to the
8128  *		    command's result list on each EOL
8129  */
8130 int
peInterpWritec(int c)8131 peInterpWritec(int c)
8132 {
8133     unsigned char ch = (unsigned char) (0xff & c);
8134 
8135     if(ch == '\n')
8136       return(peInterpFlush() == TCL_OK);
8137     else
8138       so_writec(ch, peED.store);
8139 
8140     return(1);
8141 }
8142 
8143 
8144 /*
8145  * peInterpFlush - write accumulated line to result object mapping
8146  *		   embedded data into exportable tcl list members
8147  *
8148  */
8149 int
peInterpFlush(void)8150 peInterpFlush(void)
8151 {
8152     char    *line, *p, *tp, *tp2, col1[32], col2[32];
8153     Tcl_Obj *lobjp, *objColor, *objPair;
8154 
8155     line = (char *) so_text(peED.store);
8156 
8157     if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){
8158 	if((p = strindex(line, TAG_EMBED)) != NULL){
8159 	    do{
8160 		*p = '\0';
8161 
8162 		if(p - line)
8163 		  peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8164 
8165 		switch(*++p){
8166 		  case TAG_HANDLE :
8167 		    {
8168 			int	  i, n;
8169 			HANDLE_S *h;
8170 
8171 
8172 			for(n = 0, i = *++p; i > 0; i--)
8173 			  n = (n * 10) + (*++p - '0');
8174 
8175 			line = ++p;	/* prepare for next section of line */
8176 
8177 			if(!peED.inhandle){
8178 			    peED.inhandle = 1;
8179 
8180 			    if((h = get_handle(peED.handles, n)) != NULL)
8181 			      switch(h->type){
8182 				case IMG :
8183 				{
8184 				    Tcl_Obj *llObj, *rObj;
8185 
8186 				    llObj = Tcl_NewListObj(0, NULL);
8187 				    Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1));
8188 
8189 				    rObj = Tcl_NewListObj(0, NULL);
8190 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1));
8191 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1));
8192 
8193 				    Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8194 
8195 				    Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8196 				    peED.inhandle = 0;
8197 				}
8198 
8199 				break;
8200 
8201 				case URL :
8202 				{
8203 				    Tcl_Obj *llObj, *rObj;
8204 
8205 				    llObj = Tcl_NewListObj(0, NULL);
8206 				    Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1));
8207 
8208 				    rObj = Tcl_NewListObj(0, NULL);
8209 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1));
8210 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1));
8211 
8212 				    Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8213 
8214 				    Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8215 				}
8216 
8217 				break;
8218 
8219 				case Attach :
8220 				{
8221 				    Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj;
8222 
8223 				    alObj = Tcl_NewListObj(0, NULL);
8224 				    Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1));
8225 
8226 				    peGetMimeTyping(mail_body(wps_global->mail_stream,
8227 							      peSequenceNumber(peED.uid),
8228 							      (unsigned char *) h->h.attach->number),
8229 						    &tObj, &stObj, &fnObj, &eObj);
8230 
8231 
8232 				    rObj = Tcl_NewListObj(0, NULL);
8233 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid));
8234 				    Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1));
8235 				    Tcl_ListObjAppendElement(peED.interp, rObj, tObj);
8236 				    Tcl_ListObjAppendElement(peED.interp, rObj, stObj);
8237 				    Tcl_ListObjAppendElement(peED.interp, rObj, fnObj);
8238 				    Tcl_ListObjAppendElement(peED.interp, rObj, eObj);
8239 
8240 				    Tcl_ListObjAppendElement(peED.interp, alObj, rObj);
8241 
8242 				    Tcl_ListObjAppendElement(peED.interp, lobjp, alObj);
8243 				}
8244 
8245 				break;
8246 
8247 				default :
8248 				  break;
8249 			      }
8250 			}
8251 		    }
8252 
8253 		    break;
8254 
8255 		  case TAG_FGCOLOR :
8256 		    if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){
8257 			/* look ahead */
8258 			if(p[11] == TAG_EMBED
8259 			   && p[12] == TAG_BGCOLOR
8260 			   && (tp2 = peColorStr(p + 13, col2))){
8261 			    objColor = Tcl_NewListObj(0, NULL);
8262 			    objPair  = Tcl_NewListObj(0, NULL);
8263 			    Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8264 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8265 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8266 			    Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8267 			    Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8268 			    strcpy(peED.color.bg, tp2);
8269 			    p += 13;
8270 			}
8271 			else if(strcmp(peED.color.bg, peED.color.bgdef)){
8272 			    objColor = Tcl_NewListObj(0, NULL);
8273 			    objPair  = Tcl_NewListObj(0, NULL);
8274 			    Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8275 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8276 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1));
8277 			    Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8278 			    Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8279 			    strcpy(peED.color.bg, peED.color.bgdef);
8280 			}
8281 			else
8282 			  peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp);
8283 
8284 			strcpy(peED.color.fg, tp);
8285 		    }
8286 
8287 		    line = p + 11;
8288 		    break;
8289 
8290 		  case TAG_BGCOLOR :
8291 		    if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){
8292 			/* look ahead */
8293 			if(p[11] == TAG_EMBED
8294 			   && p[12] == TAG_FGCOLOR
8295 			   && (tp2 = peColorStr(p + 13, col2))){
8296 			    objColor = Tcl_NewListObj(0, NULL);
8297 			    objPair  = Tcl_NewListObj(0, NULL);
8298 			    Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8299 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8300 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8301 			    Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8302 			    Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8303 			    strcpy(peED.color.fg, tp2);
8304 			    p += 13;
8305 			}
8306 			else if(strcmp(peED.color.fg, peED.color.fgdef)){
8307 			    objColor = Tcl_NewListObj(0, NULL);
8308 			    objPair  = Tcl_NewListObj(0, NULL);
8309 			    Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8310 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1));
8311 			    Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8312 			    Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8313 			    Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8314 			    strcpy(peED.color.fg, peED.color.fgdef);
8315 			}
8316 			else
8317 			  peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp);
8318 
8319 			strcpy(peED.color.bg, tp);
8320 		    }
8321 
8322 		    line = p + 11;
8323 		    break;
8324 
8325 		  case TAG_ITALICON :
8326 		    peAppListF(peED.interp, lobjp, "%s%s", "italic", "on");
8327 		    line = p + 1;
8328 		    break;
8329 
8330 		  case TAG_ITALICOFF :
8331 		    peAppListF(peED.interp, lobjp, "%s%s", "italic", "off");
8332 		    line = p + 1;
8333 		    break;
8334 
8335 		  case TAG_BOLDON :
8336 		    peAppListF(peED.interp, lobjp, "%s%s", "bold", "on");
8337 		    line = p + 1;
8338 		    break;
8339 
8340 		  case TAG_BOLDOFF :
8341 		    peAppListF(peED.interp, lobjp, "%s%s", "bold", "off");
8342 		    line = p + 1;
8343 		    break;
8344 
8345 		  case TAG_ULINEON :
8346 		    peAppListF(peED.interp, lobjp, "%s%s", "underline", "on");
8347 		    line = p + 1;
8348 		    break;
8349 
8350 		  case TAG_ULINEOFF :
8351 		    peAppListF(peED.interp, lobjp, "%s%s", "underline", "off");
8352 		    line = p + 1;
8353 		    break;
8354 
8355 		  case TAG_STRIKEON :
8356 		    peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on");
8357 		    line = p + 1;
8358 		    break;
8359 
8360 		  case TAG_STRIKEOFF :
8361 		    peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off");
8362 		    line = p + 1;
8363 		    break;
8364 
8365 		  case TAG_BIGON :
8366 		    peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on");
8367 		    line = p + 1;
8368 		    break;
8369 
8370 		  case TAG_BIGOFF :
8371 		    peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off");
8372 		    line = p + 1;
8373 		    break;
8374 
8375 		  case TAG_SMALLON :
8376 		    peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on");
8377 		    line = p + 1;
8378 		    break;
8379 
8380 		  case TAG_SMALLOFF :
8381 		    peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off");
8382 		    line = p + 1;
8383 		    break;
8384 
8385 		  case TAG_INVOFF :
8386 		  case TAG_HANDLEOFF :
8387 		    if(peED.inhandle){
8388 			peAppListF(peED.interp, lobjp, "%s%s", "urlend", "");
8389 			peED.inhandle = 0;
8390 		    }
8391 		    /* fall thru and advance "line" */
8392 
8393 		  default :
8394 		    line = p + 1;
8395 		    break;
8396 		}
8397 
8398 	    }
8399 	    while((p = strindex(line, TAG_EMBED)) != NULL);
8400 
8401 	    if(*line)
8402 	      peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8403 	}
8404 	else
8405 	  peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8406     }
8407     else
8408       peAppListF(peED.interp, lobjp, "%s%s", "t", "");
8409 
8410     if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){
8411 	so_truncate(peED.store, 0L);
8412 	return(TCL_OK);
8413     }
8414 
8415     return(TCL_ERROR);
8416 }
8417 
8418 
8419 
8420 /*
8421  * peInterpWritec - collect filtered output, appending to the
8422  *		    command's result list on each EOL
8423  */
8424 int
peNullWritec(int c)8425 peNullWritec(int c)
8426 {
8427     return(1);
8428 }
8429 
8430 
8431 char *
peColorStr(char * s,char * b)8432 peColorStr(char *s, char *b)
8433 {
8434     int i, j, color;
8435 
8436     i = 0;
8437     b[0] = '\0';
8438     while(1){
8439 	color = 0;
8440 	for(j = 0; j < 3; j++, s++)
8441 	  if(isdigit((unsigned char) *s))
8442 	    color = (color * 10) + (*s - '0');
8443 
8444 	s++;				/* advance past ',' */
8445 	if(color < 256)
8446 	  sprintf(b + strlen(b), "%2.2x", color);
8447 	else
8448 	  break;
8449 
8450 	if(++i == 3)
8451 	  return(b);
8452     }
8453 
8454 
8455     return(NULL);
8456 }
8457 
8458 
8459 /*
8460  * returns a list of elements
8461  */
8462 int
peMessageHeader(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8463 peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8464 {
8465     MESSAGECACHE *mc;
8466     HEADER_S	  h;
8467     int		  flags, rv = TCL_OK;
8468     long	  raw;
8469 #if	0
8470     char	 *color;
8471 #endif
8472 
8473     /*
8474      * ONLY full header mode (raw) output should get written to the
8475      * writec function we pass format_header.  If there's something
8476      * in the store after formatting ,we'll write it to the Tcl result
8477      * then, not as its accumulated
8478      */
8479     peED.interp = interp;
8480     peED.obj	= Tcl_NewStringObj("", -1);
8481 
8482     if(peED.store)
8483       so_seek(peED.store, 0L, 0);
8484     else
8485       peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8486 
8487     flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8488 
8489 #if	0
8490     peED.color.fg[0] = '\0';
8491     if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8492 	peInterpWritec(TAG_EMBED);
8493 	peInterpWritec(TAG_FGCOLOR);
8494 	gf_puts(color, peInterpWritec);
8495 	strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
8496     }
8497 
8498     peED.color.bg[0] = '\0';
8499     if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8500 	peInterpWritec(TAG_EMBED);
8501 	peInterpWritec(TAG_BGCOLOR);
8502 	gf_puts(color, peInterpWritec);
8503 	strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
8504     }
8505 
8506     peInterpFlush();
8507 #endif
8508 
8509     raw = peSequenceNumber(uid);
8510     if(peED.uid != uid){
8511 	peED.uid  = uid;
8512 	peED.body = NULL;
8513 
8514 	wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8515 	if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8516 	     && (mc = mail_elt(wps_global->mail_stream, raw)))){
8517 	    char buf[256];
8518 
8519 	    snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8520 		     wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8521 
8522 	    dprint((1, "ERROR fetching %s of msg %ld: %s",
8523 		    peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8524 		    wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8525 
8526 	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
8527 	    rv = TCL_ERROR;
8528 	}
8529 	else{
8530 	    zero_atmts(wps_global->atmts);
8531 #ifdef SMIME
8532 	    if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8533 	      wps_global->smime->need_passphrase = 0;
8534 
8535 	    fiddle_smime_message(peED.body, raw);
8536 #endif
8537 	    describe_mime(peED.body, "", 1, 1, 0, flags);
8538 	}
8539     }
8540 
8541     /* NO HANDLES init_handles(&peED.handles);*/
8542 
8543     /*
8544      * Collect header pieces into lists via the passed custom formatter.  Collect
8545      * everything else in the storage object passed.  The latter should only end up
8546      * with raw header data.
8547      *
8548      * BUG: DEAL WITH COLORS
8549      */
8550     if(rv == TCL_OK){
8551 	HD_INIT(&h, wps_global->VAR_VIEW_HEADERS, wps_global->view_all_except, FE_DEFAULT);
8552 	if(format_header(wps_global->mail_stream, raw, NULL, peED.env, &h,
8553 			 NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){
8554 	    char buf[256];
8555 
8556 	    snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid));
8557 	    dprint((1, buf));
8558 
8559 	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
8560 	    rv = TCL_ERROR;
8561 	}
8562     }
8563 
8564     peInterpFlush();
8565     peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj);
8566 
8567     so_give(&peED.store);
8568     return(rv);
8569 }
8570 
8571 void
peFormatEnvelope(MAILSTREAM * s,long int n,char * sect,ENVELOPE * e,gf_io_t pc,long int which,char * oacs,int flags)8572 peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc, long int which, char *oacs, int flags)
8573 {
8574     char *p2, buftmp[MAILTMPLEN];
8575     Tcl_Obj *objHdr;
8576 
8577     if(!e)
8578       return;
8579 
8580     if((which & FE_DATE) && e->date) {
8581 	if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8582 	    snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date);
8583 	    buftmp[sizeof(buftmp)-1] = '\0';
8584 	    p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf, SIZEOF_20KBUF, buftmp);
8585 	    peFormatEnvelopeText("Date", p2);
8586 	}
8587 	/* BUG: how does error feedback bubble back up? */
8588     }
8589 
8590     if((which & FE_FROM) && e->from)
8591       peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc);
8592 
8593     if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from)))
8594       peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc);
8595 
8596     if((which & FE_TO) && e->to)
8597       peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc);
8598 
8599     if((which & FE_CC) && e->cc)
8600       peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc);
8601 
8602     if((which & FE_BCC) && e->bcc)
8603       peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc);
8604 
8605     if((which & FE_RETURNPATH) && e->return_path)
8606       peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc);
8607 
8608     if((which & FE_NEWSGROUPS) && e->newsgroups)
8609       peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc);
8610 
8611     if((which & FE_FOLLOWUPTO) && e->followup_to)
8612       peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc);
8613 
8614     if((which & FE_SUBJECT) && e->subject && e->subject[0]){
8615 	if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8616 	    char *freeme = NULL;
8617 
8618 	    p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000),
8619 			   (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->subject),
8620 			   SIZEOF_20KBUF-10000);
8621 
8622 	    if(flags & FM_DISPLAY
8623 	       && (wps_global->display_keywords_in_subject
8624 		   || wps_global->display_keywordinits_in_subject)){
8625 
8626 		/* don't bother if no keywords are defined */
8627 		if(some_user_flags_defined(s))
8628 		  p2 = freeme = prepend_keyword_subject(s, n, p2,
8629 							wps_global->display_keywords_in_subject ? KW : KWInit,
8630 							NULL, wps_global->VAR_KW_BRACES);
8631 	    }
8632 
8633 	    peFormatEnvelopeText("Subject", p2);
8634 
8635 	    if(freeme)
8636 	      fs_give((void **) &freeme);
8637 	}
8638     }
8639 
8640     if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from)))
8641       peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc);
8642 
8643     if((which & FE_MESSAGEID) && e->message_id){
8644 	p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
8645 	peFormatEnvelopeText("Message-ID", p2);
8646     }
8647 
8648     if((which & FE_INREPLYTO) && e->in_reply_to){
8649 	p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
8650 	peFormatEnvelopeText("In-Reply-To", p2);
8651     }
8652 
8653     if((which & FE_REFERENCES) && e->references) {
8654 	p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
8655 	peFormatEnvelopeText("References", p2);
8656     }
8657 }
8658 
8659 
8660 /*
8661  * appends caller's result with: {"text" field_name {field_value}}
8662  */
8663 void
peFormatEnvelopeText(char * field_name,char * field_value)8664 peFormatEnvelopeText(char *field_name, char *field_value)
8665 {
8666     peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value);
8667 }
8668 
8669 
8670 /*
8671  * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }}
8672  *				 {"rawaddr" field_name {{raw_address} ... }}
8673  */
8674 void
peFormatEnvelopeAddress(MAILSTREAM * stream,long int msgno,char * section,char * field_name,struct mail_address * addr,int flags,char * oacs,gf_io_t pc)8675 peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
8676 			struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
8677 {
8678     char    *ptmp, *mtmp, *atype = "addr";
8679     int	     group = 0;
8680     ADDRESS *atmp;
8681     Tcl_Obj *objAddrList = NULL;
8682     STORE_S *tso;
8683     gf_io_t  tpc;
8684     extern const char *rspecials;
8685     extern const char *rspecials_minus_quote_and_dot;
8686 
8687     if(!addr)
8688       return;
8689 
8690     /*
8691      * quickly run down address list to make sure none are patently bogus.
8692      * If so, just blat raw field out.
8693      */
8694     for(atmp = addr; stream && atmp; atmp = atmp->next)
8695       if(atmp->host && atmp->host[0] == '.'){
8696 	  char *field, *fields[2];
8697 
8698 	  atype = "rawaddr";
8699 	  if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL)
8700 	    return;	       /* BUG: handle list creation failure */
8701 
8702 	  fields[1] = NULL;
8703 	  fields[0] = cpystr(field_name);
8704 	  if((ptmp = strchr(fields[0], ':')) != NULL)
8705 	    *ptmp = '\0';
8706 
8707 	  if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
8708 	      char *h, *t;
8709 
8710 	      for(t = h = field; *h ; t++)
8711 		if(*t == '\015' && *(t+1) == '\012'){
8712 		    *t = '\0';			/* tie off line */
8713 
8714 		    Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8715 
8716 		    if(!*(h = (++t) + 1))	/* set new h and skip CRLF */
8717 		      break;			/* no more to write */
8718 		}
8719 		else if(!*t){			/* shouldn't happen much */
8720 		    if(h != t)
8721 		      Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8722 
8723 		    break;
8724 		}
8725 
8726 	      fs_give((void **)&field);
8727 	  }
8728 
8729 	  fs_give((void **)&fields[0]);
8730       }
8731 
8732     if(!objAddrList){
8733 	if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL)
8734 	  return;	       /* BUG: handle list creation failure */
8735 
8736 	gf_set_so_writec(&tpc, tso);
8737 
8738 	while(addr){
8739 
8740 	    atmp	   = addr->next;		/* remember what's next */
8741 	    addr->next     = NULL;
8742 	    if(!addr->host && addr->mailbox){
8743 		mtmp = addr->mailbox;
8744 		addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
8745 					       (unsigned char *)wtmp_20k_buf,
8746 					       SIZEOF_20KBUF, addr->mailbox));
8747 	    }
8748 
8749 	    ptmp	       = addr->personal;	/* RFC 1522 personal name? */
8750 	    addr->personal = iutf8ncpy((char *)wtmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(wtmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
8751 	    wtmp_20k_buf[10000-1] = '\0';
8752 
8753 
8754 	    /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */
8755 	    if (addr->host) {		/* ordinary address? */
8756 		if (!(addr->personal || addr->adl)){
8757 		    so_seek(tso, 0L, 0);
8758 		    pine_rfc822_address (addr, tpc);
8759 		    peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8760 		}
8761 		else {			/* no, must use phrase <route-addr> form */
8762 		    Tcl_Obj *objTmp;
8763 
8764 		    if (addr->personal){
8765 			so_seek(tso, 0L, 0);
8766 			pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc);
8767 			objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso));
8768 		    }
8769 
8770 		    so_seek(tso, 0L, 0);
8771 		    pine_rfc822_address(addr, tpc);
8772 		    peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8773 		}
8774 
8775 		if(group)
8776 		  group++;
8777 	    }
8778 	    else if (addr->mailbox) {	/* start of group? */
8779 		so_seek(tso, 0L, 0);
8780 		/* yes, write group name */
8781 		pine_rfc822_cat (addr->mailbox, rspecials, tpc);
8782 		peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), "");
8783 		group = 1;		/* in a group */
8784 	    }
8785 	    else if (group) {		/* must be end of group (but be paranoid) */
8786 		peAppListF(peED.interp, objAddrList, "%s%s", "", ";");
8787 		group = 0;		/* no longer in that group */
8788 	    }
8789 
8790 	    addr->personal = ptmp;			/* restore old personal ptr */
8791 	    if(!addr->host && addr->mailbox){
8792 		fs_give((void **)&addr->mailbox);
8793 		addr->mailbox = mtmp;
8794 	    }
8795 
8796 	    addr->next = atmp;
8797 	    addr       = atmp;
8798 	}
8799 
8800 	gf_clear_so_writec(tso);
8801 	so_give(&tso);
8802     }
8803 
8804     peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList);
8805 }
8806 
8807 
8808 /*
8809  * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }}
8810  */
8811 void
peFormatEnvelopeNewsgroups(char * field_name,char * newsgrps,int flags,gf_io_t pc)8812 peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_io_t pc)
8813 {
8814     char     buf[MAILTMPLEN];
8815     int	     llen;
8816     char    *next_ng;
8817     Tcl_Obj *objNewsgroups;
8818 
8819     /* BUG: handle list creation failure */
8820     if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL)
8821       return;
8822 
8823     llen = strlen(field_name);
8824     while(*newsgrps){
8825         for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++)
8826 	  ;
8827 
8828         strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
8829         buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
8830 
8831 	Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1));
8832 
8833         newsgrps = next_ng;
8834         if(*newsgrps)
8835           newsgrps++;
8836     }
8837 
8838     peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups);
8839 }
8840 
8841 
8842 int
peMessageAttachments(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8843 peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8844 {
8845     MESSAGECACHE *mc;
8846     ATTACH_S	 *a;
8847     BODY	 *body;
8848     Tcl_Obj	 *objAtt, *tObj, *stObj, *fnObj;
8849     int		  flags, rv = TCL_OK;
8850     long	  raw;
8851 
8852     peED.interp = interp;
8853     peED.obj	= Tcl_GetObjResult(interp);
8854 
8855     flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER;
8856 
8857     raw = peSequenceNumber(uid);
8858 
8859     if(peED.uid != uid){
8860 	memset(&peED, 0, sizeof(peED));
8861 
8862 	peED.uid  = uid;
8863 
8864 	wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8865 	if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8866 	     && (mc = mail_elt(wps_global->mail_stream, raw)))){
8867 	    char buf[256];
8868 
8869 	    snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8870 		     wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8871 
8872 	    dprint((1, "ERROR fetching %s of msg %ld: %s",
8873 		    peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8874 		    wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8875 
8876 	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
8877 	    rv = TCL_ERROR;
8878 	}
8879 	else{
8880 	    zero_atmts(wps_global->atmts);
8881 #ifdef SMIME
8882 	    if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8883 	      wps_global->smime->need_passphrase = 0;
8884 
8885 	    fiddle_smime_message(peED.body, raw);
8886 #endif
8887 	    describe_mime(peED.body, "", 1, 1, 0, flags);
8888 	}
8889     }
8890 
8891     /* package up attachment list */
8892     for(a = wps_global->atmts; rv == TCL_OK && a->description != NULL; a++)
8893       if((objAtt = Tcl_NewListObj(0, NULL)) != NULL
8894 	 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){
8895 	  peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
8896 
8897 	  if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK
8898 	       && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK
8899 	       && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK
8900 	       && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK
8901 	       && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK
8902 	       && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK
8903 	       && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK))
8904 	    rv = TCL_ERROR;
8905       }
8906       else
8907 	rv = TCL_ERROR;
8908 
8909     return(rv);
8910 }
8911 
8912 
8913 int
peMessageBody(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)8914 peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8915 {
8916     MESSAGECACHE *mc;
8917     int		  flags, rv = TCL_OK;
8918     long	  raw;
8919     char	 *color;
8920 
8921     peED.interp = interp;
8922     peED.obj	= Tcl_GetObjResult(interp);
8923 
8924     if(peED.store)
8925       so_seek(peED.store, 0L, 0);
8926     else
8927       peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8928 
8929     flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8930 
8931     if(objc == 1 && objv[0]){			/* flags */
8932 	int	  i, nFlags;
8933 	Tcl_Obj **objFlags;
8934 	char	 *flagstr;
8935 
8936 	Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags);
8937 	for(i = 0; i < nFlags; i++){
8938 	    if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
8939 		rv = TCL_ERROR;
8940 	    }
8941 
8942 	    if(!strucmp(flagstr, "html"))
8943 	      flags |= (FM_HTML | FM_HIDESERVER);
8944 	    else if(!strucmp(flagstr, "images"))
8945 	      flags |= (FM_HTMLIMAGES);
8946 	}
8947     }
8948 
8949     peED.color.fg[0] = '\0';
8950     if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8951 	peInterpWritec(TAG_EMBED);
8952 	peInterpWritec(TAG_FGCOLOR);
8953 	gf_puts(color, peInterpWritec);
8954 	strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
8955     }
8956 
8957     peED.color.bg[0] = '\0';
8958     if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8959 	peInterpWritec(TAG_EMBED);
8960 	peInterpWritec(TAG_BGCOLOR);
8961 	gf_puts(color, peInterpWritec);
8962 	strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
8963     }
8964 
8965     peInterpFlush();
8966 
8967     init_handles(&peED.handles);
8968 
8969     raw = peSequenceNumber(uid);
8970 
8971     if(peED.uid != uid){
8972 	peED.uid  = uid;
8973 	peED.body = NULL;
8974 
8975 	wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8976 	if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8977 	     && (mc = mail_elt(wps_global->mail_stream, raw)))){
8978 	    char buf[256];
8979 
8980 	    snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8981 		     wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8982 
8983 	    dprint((1, "ERROR fetching %s of msg %ld: %s",
8984 		    peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8985 		    wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8986 
8987 	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
8988 	    rv = TCL_ERROR;
8989 	}
8990 	else{
8991 	    zero_atmts(wps_global->atmts);
8992 #ifdef SMIME
8993 	    if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8994 	      wps_global->smime->need_passphrase = 0;
8995 
8996 	    fiddle_smime_message(peED.body, raw);
8997 #endif
8998 	    describe_mime(peED.body, "", 1, 1, 0, flags);
8999 	}
9000     }
9001 
9002     /* format message body */
9003     if(rv == TCL_OK){
9004 	HEADER_S  h;
9005 	char	 *errstr;
9006 
9007 	HD_INIT(&h, wps_global->VAR_VIEW_HEADERS, wps_global->view_all_except, FE_DEFAULT);
9008 #ifdef SMIME
9009 	/* kind of a hack, the description maybe shouldn't be in the editorial stuff */
9010 	if(wps_global->smime && wps_global->smime->need_passphrase)
9011 	  flags &= ~FM_NOEDITORIAL;
9012 #endif
9013 	if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){
9014 	    gf_puts(errstr, peInterpWritec);
9015 	    rv = TCL_ERROR;
9016 	}
9017     }
9018 
9019     peInterpFlush();
9020 
9021     so_give(&peED.store);
9022     free_handles(&peED.handles);
9023     return(rv);
9024 }
9025 
9026 
9027 int
peMessageText(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9028 peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9029 {
9030     MESSAGECACHE *mc;
9031     ENVELOPE	 *env;
9032     BODY	 *body;
9033     int		  flags;
9034     long	  raw;
9035     char	 *color;
9036 
9037     memset(&peED, 0, sizeof(peED));
9038     peED.interp = interp;
9039     peED.obj    = Tcl_GetObjResult(interp);
9040     peED.store  = so_get(CharStar, NULL, EDIT_ACCESS);
9041 
9042     peED.color.fg[0] = '\0';
9043     if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
9044 	peInterpWritec(TAG_EMBED);
9045 	peInterpWritec(TAG_FGCOLOR);
9046 	gf_puts(color, peInterpWritec);
9047 	strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
9048     }
9049 
9050     peED.color.bg[0] = '\0';
9051     if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
9052 	peInterpWritec(TAG_EMBED);
9053 	peInterpWritec(TAG_BGCOLOR);
9054 	gf_puts(color, peInterpWritec);
9055 	strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
9056     }
9057 
9058     raw	     = peSequenceNumber(peED.uid = uid);
9059     body     = NULL;
9060     wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
9061     if(!((env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &body))
9062 	 && (mc = mail_elt(wps_global->mail_stream, raw)))){
9063 	char buf[256];
9064 
9065 	snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
9066 		wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
9067 
9068 	dprint((1, "ERROR fetching %s of msg %ld: %s",
9069 		   env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
9070 		   wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
9071 
9072 	Tcl_SetResult(interp, buf, TCL_VOLATILE);
9073 	return(TCL_ERROR);
9074     }
9075 
9076     flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
9077 
9078     init_handles(&peED.handles);
9079 
9080     (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec);
9081 
9082     peInterpFlush();
9083 
9084     so_give(&peED.store);
9085     free_handles(&peED.handles);
9086     return(TCL_OK);
9087 }
9088 
9089 
9090 /*
9091  * peMessagePartFromCID - return part number assoc'd with given uid and CID
9092  */
9093 int
peMessagePartFromCID(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9094 peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9095 {
9096     char     *cid, sect_buf[256];
9097     long      raw;
9098     ENVELOPE *env;
9099     BODY     *body;
9100 
9101     raw	= peSequenceNumber(peED.uid = uid);
9102     wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
9103 
9104     if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){
9105 	if((env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &body)) != NULL){
9106 	    sect_buf[0] = '\0';
9107 	    if(peLocateBodyByCID(cid, sect_buf, body)){
9108 		Tcl_SetResult(interp, sect_buf, TCL_VOLATILE);
9109 	    }
9110 	}
9111 	else{
9112 	    Tcl_SetResult(interp, wps_global->last_error[0] ? wps_global->last_error : "Error getting CID", TCL_VOLATILE);
9113 	    return(TCL_ERROR);
9114 	}
9115     }
9116 
9117     return(TCL_OK);
9118 }
9119 
9120 
9121 int
peLocateBodyByCID(char * cid,char * section,BODY * body)9122 peLocateBodyByCID(char *cid, char *section, BODY *body)
9123 {
9124     if(body->type == TYPEMULTIPART){
9125 	char  subsection[256], *subp;
9126 	int   n;
9127 	PART *part     = body->nested.part;
9128 
9129 	if(!(part = body->nested.part))
9130 	  return(0);
9131 
9132 	subp = subsection;
9133 	if(section && *section){
9134 	    for(n = 0;
9135 		n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
9136 	      ;
9137 
9138 	    *subp++ = '.';
9139 	}
9140 
9141 	n = 1;
9142 	do {
9143 	    sprintf(subp, "%d", n++);
9144 	    if(peLocateBodyByCID(cid, subsection, &part->body)){
9145 		strcpy(section, subsection);
9146 		return(1);
9147 	    }
9148 	}
9149 	while((part = part->next) != NULL);
9150 
9151 	return(0);
9152     }
9153 
9154     return((body && body->id) ? !strcmp(cid, body->id) : 0);
9155 }
9156 
9157 
9158 /*
9159  * peGetFlag - Return 1 or 0 based on requested flags current state
9160  *
9161  * Params: argv[0] == flagname
9162  */
9163 int
peGetFlag(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9164 peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9165 {
9166     char *flagname;
9167 
9168     Tcl_SetResult(interp,
9169 		  int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9170 			       ? peIsFlagged(wps_global->mail_stream, uid, flagname)
9171 			       : 0),
9172 		  TCL_VOLATILE);
9173     return(TCL_OK);
9174 }
9175 
9176 
9177 int
peIsFlagged(MAILSTREAM * stream,imapuid_t uid,char * flagname)9178 peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname)
9179 {
9180     MESSAGECACHE *mc;
9181     long	  raw = peSequenceNumber(uid);
9182 
9183     if(!((mc = mail_elt(stream, raw)) && mc->valid)){
9184 	mail_fetch_flags(stream, ulong2string(uid), FT_UID);
9185 	mc = mail_elt(stream, raw);
9186     }
9187 
9188     if(!strucmp(flagname, "deleted"))
9189       return(mc->deleted);
9190 
9191     if(!strucmp(flagname, "new"))
9192       return(!mc->seen);
9193 
9194     if(!strucmp(flagname, "important"))
9195       return(mc->flagged);
9196 
9197     if(!strucmp(flagname, "answered"))
9198       return(mc->answered);
9199 
9200     if(!strucmp(flagname, "recent"))
9201       return(mc->recent);
9202 
9203     return(0);
9204 }
9205 
9206 
9207 /*
9208  * peSetFlag - Set requested flags value to 1 or 0
9209  *
9210  * Params: abjv[0] == flagname
9211  *         objv[1] == newvalue
9212  */
9213 int
peSetFlag(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9214 peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9215 {
9216     char	 *flagname, *flagstr = NULL;
9217     int		  value;
9218 
9219     if((flagname = Tcl_GetStringFromObj(objv[0], NULL))
9220        && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){
9221 	if(!strucmp(flagname, "deleted")){
9222 	    flagstr = "\\DELETED";
9223 	}
9224 	else if(!strucmp(flagname, "new")){
9225 	    flagstr = "\\SEEN";
9226 	    value = !value;
9227 	}
9228 	else if(!strucmp(flagname, "important")){
9229 	    flagstr = "\\FLAGGED";
9230 	}
9231 	else if(!strucmp(flagname, "answered")){
9232 	    flagstr = "\\ANSWERED";
9233 	}
9234 	else if(!strucmp(flagname, "recent")){
9235 	    flagstr = "\\RECENT";
9236 	}
9237 
9238 	if(flagstr){
9239 	    wps_global->c_client_error[0] = '\0';
9240 	    mail_flag(wps_global->mail_stream,
9241 		      ulong2string(uid),
9242 		      flagstr, (value ? ST_SET : 0L) | ST_UID);
9243 	    if(wps_global->c_client_error[0] != '\0'){
9244 		snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s",
9245 			 wps_global->c_client_error);
9246 		Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
9247 		return(TCL_ERROR);
9248 	    }
9249 	}
9250     }
9251 
9252     Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC);
9253     return(TCL_OK);
9254 }
9255 
9256 
9257 /*
9258  * peMsgSelect - Return 1 or 0 based on whether given UID is selected
9259  *
9260  * Params: argv[0] == selected
9261  */
9262 int
peMsgSelect(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9263 peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9264 {
9265     int value;
9266 
9267     if(objc == 1 && objv[0]){
9268 	if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){
9269 	    if(value){
9270 		set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9271 			  peMessageNumber(uid), MN_SLCT, 1);
9272 		set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9273 			  peMessageNumber(uid), MN_HIDE, 0);
9274 	    } else {
9275 		set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9276 			  peMessageNumber(uid), MN_SLCT, 0);
9277 		/* if zoomed, lite hidden bit */
9278 		if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
9279 		  set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9280 			    peMessageNumber(uid), MN_HIDE, 1);
9281 
9282 	    }
9283 	}
9284 	else{
9285 	    Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC);
9286 	    return(TCL_ERROR);
9287 	}
9288     }
9289 
9290     Tcl_SetResult(interp,
9291 		  (get_lflag(wps_global->mail_stream, NULL,
9292 			     peSequenceNumber(uid),
9293 			     MN_SLCT))
9294 		    ? "1" : "0",
9295 		  TCL_VOLATILE);
9296     return(TCL_OK);
9297 }
9298 
9299 
9300 /*
9301  * peAppendIndexParts - append list of digested index pieces to given object
9302  *
9303  * Params:
9304  *
9305  */
9306 int
peAppendIndexParts(Tcl_Interp * interp,imapuid_t uid,Tcl_Obj * aObj,int * fetched)9307 peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9308 {
9309     Tcl_Obj	*objField, *objElement, *objp;
9310     ICE_S	*h;
9311     IFIELD_S    *f;
9312     IELEM_S	*ie;
9313 
9314 
9315     if((h = build_header_work(wps_global, wps_global->mail_stream,
9316 			      sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9317 			      gPeITop, gPeICount, fetched)) != NULL){
9318 	for(f = h->ifield; f; f = f->next){
9319 
9320 	    if((objField = Tcl_NewListObj(0, NULL)) == NULL)
9321 	      return(TCL_ERROR);
9322 
9323 	    for(ie = f->ielem; ie ; ie = ie->next){
9324 
9325 		if((objElement = Tcl_NewListObj(0, NULL)) == NULL)
9326 		  return(TCL_ERROR);
9327 
9328 		if(ie->datalen){
9329 		    /* FIRST: DATA */
9330 #if	INTERNAL_INDEX_TRUNCATE
9331 		    char *ep;
9332 
9333 		    ep = (char *) fs_get((ie->datalen + 1) * sizeof(char));
9334 		    sprintf(ep, "%.*s", ie->wid, ie->data);
9335 
9336 		    /* and other stuff to pack trunc'd element into a new object */
9337 #endif
9338 
9339 		    objp = Tcl_NewStringObj(ie->data, ie->datalen);
9340 		}
9341 		else
9342 		  objp = Tcl_NewStringObj("", -1);
9343 
9344 		if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9345 		  return(TCL_ERROR);
9346 
9347 		if(ie->color){
9348 		    Tcl_Obj *objColor;
9349 		    char     hexcolor[32];
9350 
9351 		    if((objp = Tcl_NewListObj(0, NULL)) == NULL)
9352 		      return(TCL_ERROR);
9353 
9354 		    hex_colorstr(hexcolor, ie->color->fg);
9355 		    objColor = Tcl_NewStringObj(hexcolor, -1);
9356 		    if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9357 		      return(TCL_ERROR);
9358 
9359 		    hex_colorstr(hexcolor, ie->color->bg);
9360 		    objColor = Tcl_NewStringObj(hexcolor, -1);
9361 		    if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9362 		      return(TCL_ERROR);
9363 		}
9364 		else
9365 		  objp = Tcl_NewStringObj("", -1);
9366 
9367 		if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9368 		  return(TCL_ERROR);
9369 
9370 		/*
9371 		 * IF we ever want to map the thread characters into nice
9372 		 * graphical symbols or take advantage of features like clicking
9373 		 * on a thread element to collapse and such, we need to have
9374 		 * element tagging. That's what the object creation and append
9375 		 * are placeholders for
9376 		 */
9377 		switch(ie->type){
9378 		  case eThreadInfo :
9379 		    objp = Tcl_NewStringObj("threadinfo", -1);
9380 		    break;
9381 		  case eText :
9382 		    objp = NULL;
9383 		    break;
9384 		  default :
9385 		    objp = Tcl_NewStringObj(int2string(ie->type), -1);
9386 		    break;
9387 		}
9388 
9389 		if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9390 		  return(TCL_ERROR);
9391 
9392 		if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK)
9393 		  return(TCL_ERROR);
9394 	    }
9395 
9396 	    if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){
9397 		return(TCL_ERROR);
9398 	    }
9399 	}
9400     }
9401 
9402     return(TCL_OK);
9403 }
9404 
9405 
9406 /*
9407  * peAppendIndexColor - append index line's foreground/background color
9408  *
9409  * Params:
9410  *
9411  */
9412 int
peAppendIndexColor(Tcl_Interp * interp,imapuid_t uid,Tcl_Obj * aObj,int * fetched)9413 peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9414 {
9415     char	 hexfg[32], hexbg[32];
9416     ICE_S	*h;
9417 
9418     if((h = build_header_work(wps_global, wps_global->mail_stream,
9419 			      sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9420 			      gPeITop, gPeICount, fetched))
9421        && h->color_lookup_done
9422        && h->linecolor){
9423 
9424 	hex_colorstr(hexfg, h->linecolor->fg);
9425 	hex_colorstr(hexbg, h->linecolor->bg);
9426 
9427 	return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg));
9428     }
9429 
9430     return(peAppListF(interp, aObj, "%s", ""));
9431 }
9432 
9433 
9434 /*
9435  * peMessageStatusBits - return list flags indicating pine status bits
9436  *
9437  * Params:
9438  *
9439  * Returns: list of lists where:
9440  *		*  the first element is the list of
9441  *		   field elements data
9442  *		*  the second element is a two element
9443  *		   list containing the lines foreground
9444  *		   and background colors
9445  */
9446 int
peMessageStatusBits(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9447 peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9448 {
9449     Tcl_SetResult(interp,
9450 		  peMsgStatBitString(wps_global, wps_global->mail_stream,
9451 				     sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9452 				     gPeITop, gPeICount, NULL),
9453 		  TCL_STATIC);
9454     return(TCL_OK);
9455 }
9456 
9457 
9458 char *
peMsgStatBitString(struct pine * state,MAILSTREAM * stream,MSGNO_S * msgmap,long msgno,long top_msgno,long msgcount,int * fetched)9459 peMsgStatBitString(struct pine *state,
9460 		   MAILSTREAM  *stream,
9461 		   MSGNO_S     *msgmap,
9462 		   long		msgno,
9463 		   long		top_msgno,
9464 		   long		msgcount,
9465 		   int	       *fetched)
9466 {
9467     static char	  buf[36];
9468     int		  i;
9469     long	  raw;
9470     MESSAGECACHE *mc;
9471     ICE_S	 *h;
9472 
9473     raw = mn_m2raw(msgmap, msgno);
9474     if((h = build_header_work(state, stream, msgmap,
9475 			      msgno, top_msgno, msgcount, fetched))
9476        && (mc = mail_elt(stream, raw))){
9477 	/* return a string representing a bit field where:
9478 	   index     meaning
9479 	   -----     -------
9480 	   0         "New"
9481 	   1         deleted
9482 	   2         answered
9483 	   3         flagged
9484 	   4         to us
9485 	   5         cc us
9486 	   6	     recent
9487 	   7	     forwarded
9488 	   8	     attachments
9489 	*/
9490 	i = 0;
9491 	buf[i++] = (mc->seen) ? '0' : '1';
9492 	buf[i++] = (mc->deleted) ? '1' : '0';
9493 	buf[i++] = (mc->answered) ? '1' : '0';
9494 	buf[i++] = (mc->flagged) ? '1' : '0';
9495 	buf[i++] = (h->to_us) ? '1' : '0';
9496 	buf[i++] = (h->cc_us) ? '1' : '0';
9497 	buf[i++] = (mc->recent) ? '1' : '0';
9498 	buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0';
9499 	buf[i++] = '0';
9500 	buf[i++] = '\0';
9501 
9502 	return(buf);
9503     }
9504 
9505     return("100000000");
9506 }
9507 
9508 
9509 Tcl_Obj *
peMsgStatNameList(Tcl_Interp * interp,struct pine * state,MAILSTREAM * stream,MSGNO_S * msgmap,long msgno,long top_msgno,long msgcount,int * fetched)9510 peMsgStatNameList(Tcl_Interp *interp,
9511 		  struct pine *state,
9512 		  MAILSTREAM  *stream,
9513 		  MSGNO_S     *msgmap,
9514 		  long		msgno,
9515 		  long		top_msgno,
9516 		  long		msgcount,
9517 		  int	       *fetched)
9518 {
9519     Tcl_Obj	 *objList;
9520     long	  raw;
9521     MESSAGECACHE *mc;
9522     ICE_S	 *h;
9523 
9524     objList = Tcl_NewListObj(0, NULL);
9525     raw = mn_m2raw(msgmap, msgno);
9526     if((h = build_header_work(state, stream, msgmap,
9527 			      msgno, top_msgno, msgcount, fetched))
9528        && (mc = mail_elt(stream, raw))){
9529 	if(!mc->seen)
9530 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1));
9531 
9532 	if(mc->deleted)
9533 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1));
9534 
9535 	if(mc->answered)
9536 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1));
9537 
9538 	if(mc->flagged)
9539 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1));
9540 
9541 	if(h->to_us)
9542 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1));
9543 
9544 	if(h->cc_us)
9545 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1));
9546 
9547 	if(mc->recent)
9548 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1));
9549 
9550 	if(user_flag_is_set(stream, raw, FORWARDED_FLAG))
9551 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1));
9552 
9553 	if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_SLCT))
9554 	  Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1));
9555     }
9556 
9557     return(objList);
9558 }
9559 
9560 
9561 /*
9562  * peReplyHeaders - return subject used in reply to given message
9563  *
9564  * Params:
9565  *
9566  * Returns: list of header value pairs where headers are:
9567  *          In-Reply-To:, Subject:, Cc:
9568  *
9569  */
9570 int
peReplyHeaders(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9571 peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9572 {
9573     long	 raw;
9574     int		 flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE;
9575     char	*errmsg = NULL, *fcc = NULL, *sect = NULL;
9576     ENVELOPE	*env, *outgoing;
9577     BODY	*body = NULL;
9578     ADDRESS	*saved_from, *saved_to, *saved_cc, *saved_resent;
9579 
9580     saved_from		  = (ADDRESS *) NULL;
9581     saved_to		  = (ADDRESS *) NULL;
9582     saved_cc		  = (ADDRESS *) NULL;
9583     saved_resent	  = (ADDRESS *) NULL;
9584 
9585     raw = peSequenceNumber(uid);
9586 
9587     /* if we're given a valid section number that
9588      * corresponds to a valid msg/rfc822 body part
9589      * then set up headers in attached message.
9590      */
9591     if(objc == 1 && objv[0]
9592        && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9593        && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) sect))
9594        && body->type == TYPEMESSAGE
9595        && !strucmp(body->subtype, "rfc822")){
9596 	env = body->nested.msg->env;
9597     }
9598     else{
9599 	sect = NULL;
9600 	env = mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9601     }
9602 
9603     if(env){
9604 	if(!reply_harvest(wps_global, raw, sect, env,
9605 			  &saved_from, &saved_to, &saved_cc,
9606 			  &saved_resent, &flags)){
9607 
9608 	    Tcl_SetResult(interp, "", TCL_STATIC);
9609 	    return(TCL_ERROR);
9610 	}
9611 
9612 	outgoing = mail_newenvelope();
9613 
9614 	reply_seed(wps_global, outgoing, env,
9615 		   saved_from, saved_to, saved_cc, saved_resent,
9616 		   &fcc, flags, &errmsg);
9617 	if(errmsg){
9618 	    if(*errmsg){
9619 		q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
9620 	    }
9621 
9622 	    fs_give((void **)&errmsg);
9623 	}
9624 
9625 	env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9626 
9627 	outgoing->subject = reply_subject(env->subject, NULL, 0);
9628 	outgoing->in_reply_to = reply_in_reply_to(env);
9629 
9630 	err = !(peAppListF(interp, Tcl_GetObjResult(interp),
9631 			   "%s%a", "to", outgoing->to) == TCL_OK
9632 		&& peAppListF(interp, Tcl_GetObjResult(interp),
9633 			      "%s%a", "cc", outgoing->cc) == TCL_OK
9634 		&& peAppListF(interp, Tcl_GetObjResult(interp),
9635 			      "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK
9636 		&& peAppListF(interp, Tcl_GetObjResult(interp),
9637 			      "%s%s", "subject",
9638 			      rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
9639 						     SIZEOF_20KBUF, outgoing->subject)) == TCL_OK
9640 		&& (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE));
9641 
9642 
9643 	/* Fill in x-reply-uid data and append it */
9644 	if(!err && wps_global->mail_stream->uid_validity){
9645 	    char *prefix = reply_quote_str(env);
9646 
9647 	    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "(%lu %s)(1 %lu %lu)%s",
9648 		    strlen(prefix), prefix,
9649 		    wps_global->mail_stream->uid_validity, uid,
9650 		    wps_global->mail_stream->mailbox);
9651 
9652 	    fs_give((void **) &prefix);
9653 
9654 	    err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9655 			     "x-reply-uid", wtmp_20k_buf) != TCL_OK;
9656 	}
9657 
9658 	mail_free_envelope(&outgoing);
9659 
9660 	if(err)
9661 	  return(TCL_ERROR);
9662     }
9663     else
9664       Tcl_SetResult(interp, "", TCL_VOLATILE);
9665 
9666     return(TCL_OK);
9667 }
9668 
9669 
9670 
9671 /*
9672  * peReplyText - return subject used in reply to given message
9673  *
9674  * Params:
9675  *
9676  * Returns:
9677  *
9678  */
9679 int
peReplyText(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9680 peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9681 {
9682     long	   msgno;
9683     char	  *prefix, *sect = NULL;
9684     int		   rv = TCL_OK;
9685     ENVELOPE	  *env;
9686     BODY	  *body = NULL, *orig_body;
9687     STORE_S	  *msgtext;
9688     REDRAFT_POS_S *redraft_pos = NULL;
9689     Tcl_Obj	  *objBody = NULL, *objAttach = NULL;
9690 
9691     msgno = peSequenceNumber(uid);
9692 
9693     if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9694 	Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE);
9695 	return(TCL_ERROR);
9696     }
9697 
9698     /*--- Grab current envelope ---*/
9699     /* if we're given a valid section number that
9700      * corresponds to a valid msg/rfc822 body part
9701      * then set up to reply the attached message's
9702      * text.
9703      */
9704     if(objc == 2 && objv[1]
9705        && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0'
9706        && (body = mail_body(wps_global->mail_stream, msgno, (unsigned char *) sect))
9707        && body->type == TYPEMESSAGE
9708        && !strucmp(body->subtype, "rfc822")){
9709 	env       = body->nested.msg->env;
9710 	orig_body = body->nested.msg->body;
9711     }
9712     else{
9713 	sect = NULL;
9714 	env = mail_fetchstructure(wps_global->mail_stream, msgno, &orig_body);
9715 	if(!(env && orig_body)){
9716 	    Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9717 	    return(TCL_ERROR);
9718 	}
9719     }
9720 
9721     if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9722       prefix = cpystr(prefix);
9723     else
9724       prefix = reply_quote_str(env);
9725 
9726     /*
9727      * BUG? Should there be some way to signal to reply_bddy
9728      * that we'd like it to produced format=flowed body text?
9729      * right now it's hardwired to in pine/reply.c
9730      */
9731 
9732     if((body = reply_body(wps_global->mail_stream, env, orig_body,
9733 			  msgno, sect, msgtext, prefix,
9734 			  TRUE, NULL, TRUE, &redraft_pos)) != NULL){
9735 
9736 	objBody = Tcl_NewListObj(0, NULL);
9737 
9738 	peSoStrToList(interp, objBody, msgtext);
9739 
9740 	Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9741 
9742 	/* sniff for attachments */
9743 	objAttach = peMsgAttachCollector(interp, body);
9744 
9745 	Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9746 
9747 
9748 	pine_free_body(&body);
9749     }
9750     else{
9751 	Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9752 	rv = TCL_ERROR;
9753     }
9754 
9755     fs_give((void **) &prefix);
9756 
9757     return(rv);
9758 }
9759 
9760 
9761 int
peSoStrToList(Tcl_Interp * interp,Tcl_Obj * obj,STORE_S * so)9762 peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so)
9763 {
9764     char *sp, *ep;
9765     Tcl_Obj *objp;
9766 
9767     for(ep = (char *) so_text(so); *ep; ep++){
9768 	sp = ep;
9769 
9770 	while(*ep && *ep != '\n')
9771 	  ep++;
9772 
9773 	objp = Tcl_NewStringObj(sp, ep - sp);
9774 
9775 	if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK)
9776 	  return(FALSE);
9777     }
9778 
9779     return(TRUE);
9780 }
9781 
9782 
9783 /*
9784  * peForwardHeaders - return subject used in forward of given message
9785  *
9786  * Params:
9787  *
9788  * Returns:
9789  *
9790  */
9791 int
peForwardHeaders(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9792 peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9793 {
9794 
9795     int	      result;
9796     long      raw;
9797     char     *tmp, *sect = NULL;
9798     ENVELOPE *env;
9799     BODY     *body;
9800 
9801     raw = peSequenceNumber(uid);
9802 
9803     /* if we're given a valid section number that
9804      * corresponds to a valid msg/rfc822 body part
9805      * then set up headers in attached message.
9806      */
9807     if(objc == 1 && objv[0]
9808        && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9809        && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) sect))
9810        && body->type == TYPEMESSAGE
9811        && !strucmp(body->subtype, "rfc822")){
9812 	env = body->nested.msg->env;
9813     }
9814     else{
9815 	sect = NULL;
9816 	env = mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9817     }
9818 
9819     if(env){
9820 	tmp = forward_subject(env, FS_NONE);
9821 	result = peAppListF(interp, Tcl_GetObjResult(interp),
9822 			    "%s%s", "subject", tmp);
9823 	fs_give((void **) &tmp);
9824 
9825 	/* Fill in x-reply-uid data and append it */
9826 	if(result == TCL_OK && wps_global->mail_stream->uid_validity){
9827 	    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s",
9828 		     wps_global->mail_stream->uid_validity, uid,
9829 		     wps_global->mail_stream->mailbox);
9830 	    result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9831 				"x-reply-uid", wtmp_20k_buf) != TCL_OK;
9832 	}
9833 
9834 	return(result);
9835     }
9836 
9837     Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
9838     return(TCL_ERROR);
9839 }
9840 
9841 
9842 
9843 /*
9844  * peForwardText - return body of message used in
9845  *		   forward of given message
9846  *
9847  * Params:
9848  *
9849  * Returns:
9850  *
9851  */
9852 int
peForwardText(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9853 peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9854 {
9855     long	   msgno;
9856     char	  *bodtext, *p, *sect = NULL;
9857     int		   rv = TCL_OK;
9858     ENVELOPE	  *env;
9859     BODY	  *body, *orig_body;
9860     STORE_S	  *msgtext;
9861     Tcl_Obj	  *objBody = NULL, *objAttach = NULL;
9862 
9863     msgno = peSequenceNumber(uid);
9864 
9865     if(objc == 1 && objv[0])
9866       sect = Tcl_GetStringFromObj(objv[0], NULL);
9867 
9868     if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9869 	Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE);
9870 	return(TCL_ERROR);
9871     }
9872 
9873 
9874     if(F_ON(F_FORWARD_AS_ATTACHMENT, wps_global)){
9875 	PART **pp;
9876 	long   totalsize = 0L;
9877 
9878 	/*---- New Body to start with ----*/
9879         body	   = mail_newbody();
9880         body->type = TYPEMULTIPART;
9881 
9882         /*---- The TEXT part/body ----*/
9883         body->nested.part			   = mail_newbody_part();
9884         body->nested.part->body.type		   = TYPETEXT;
9885         body->nested.part->body.contents.text.data = (unsigned char *) msgtext;
9886 
9887 	pp = &(body->nested.part->next);
9888 
9889 	/*---- The Message body subparts ----*/
9890 	env = pine_mail_fetchstructure(wps_global->mail_stream, msgno, NULL);
9891 
9892 	if(forward_mime_msg(wps_global->mail_stream, msgno,
9893 			    (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){
9894 	    totalsize = (*pp)->body.size.bytes;
9895 	    pp = &((*pp)->next);
9896 	}
9897     }
9898     else{
9899 	/*--- Grab current envelope ---*/
9900 	/* if we're given a valid section number that
9901 	 * corresponds to a valid msg/rfc822 body part
9902 	 * then set up to forward the attached message's
9903 	 * text.
9904 	 */
9905 
9906 	if(sect && *sect != '\0'
9907 	   && (body = mail_body(wps_global->mail_stream, msgno, (unsigned char *) sect))
9908 	   && body->type == TYPEMESSAGE
9909 	   && !strucmp(body->subtype, "rfc822")){
9910 	    env       = body->nested.msg->env;
9911 	    orig_body = body->nested.msg->body;
9912 	}
9913 	else{
9914 	    sect = NULL;
9915 	    env = mail_fetchstructure(wps_global->mail_stream, msgno, &orig_body);
9916 	    if(!(env && orig_body)){
9917 		Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9918 		return(TCL_ERROR);
9919 	    }
9920 	}
9921 
9922 	body = forward_body(wps_global->mail_stream, env, orig_body,
9923 			    msgno, sect, msgtext, FWD_NONE);
9924     }
9925 
9926     if(body){
9927 	bodtext = (char *) so_text(msgtext);
9928 
9929 	objBody = Tcl_NewListObj(0, NULL);
9930 
9931 	for(p = bodtext; *p; p++){
9932 	    Tcl_Obj *objp;
9933 
9934 	    bodtext = p;
9935 	    while(*p && *p != '\n')
9936 	      p++;
9937 
9938 	    objp = Tcl_NewStringObj(bodtext, p - bodtext);
9939 
9940 	    Tcl_ListObjAppendElement(interp, objBody, objp);
9941 	}
9942 
9943 	Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9944 
9945 	/* sniff for attachments */
9946 	objAttach = peMsgAttachCollector(interp, body);
9947 	Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9948 
9949 	pine_free_body(&body);
9950     }
9951     else{
9952 	Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9953 	rv = TCL_ERROR;
9954     }
9955 
9956     return(rv);
9957 }
9958 
9959 
9960 
9961 /*
9962  * peDetach -
9963  *
9964  * Params: argv[0] == attachment part number
9965  *         argv[1] == directory to hold tmp file
9966  *
9967  * Returns: list containing:
9968  *
9969  *		0) response: OK or ERROR
9970  *	     if OK
9971  *		1) attachment's mime type
9972  *		2) attachment's mime sub-type
9973  *		3) attachment's size in bytes (decoded)
9974  *		4) attachment's given file name (if any)
9975  *		5) tmp file holding raw attachment data
9976  */
9977 int
peDetach(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)9978 peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9979 {
9980     char	 *part, *err, *tfd, *tfn = NULL, *filename;
9981     long	  raw;
9982     gf_io_t	  pc;
9983     BODY	 *body;
9984     STORE_S	 *store;
9985     Tcl_Obj	 *rvobj, *tObj, *stObj, *fnObj;
9986 
9987     if((part = Tcl_GetStringFromObj(objv[0], NULL))
9988        && (raw = peSequenceNumber(uid))
9989        && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) part))){
9990 
9991 	peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
9992 
9993 	err = NULL;
9994 	if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){
9995 	    tfn = temp_nam(tfd = NULL, "pd");
9996 	}
9997 	else if(is_writable_dir(tfd) == 0){
9998 	    tfn = temp_nam(tfd, "pd");
9999 	}
10000 	else
10001 	  tfn = tfd;
10002 
10003 	filename = Tcl_GetStringFromObj(fnObj, NULL);
10004 	dprint((5, "PEDetach(name: %s, tmpfile: %s)",
10005 		   filename ? filename : "<null>", tfn));
10006 
10007 	if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){
10008 	    gf_set_so_writec(&pc, store);
10009 	    err = detach(wps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0);
10010 	    gf_clear_so_writec(store);
10011 	    so_give(&store);
10012 	}
10013 	else
10014 	  err = "Can't allocate internal storage";
10015     }
10016     else
10017       err = "Can't get message data";
10018 
10019     if(err){
10020 	if(tfn)
10021 	  unlink(tfn);
10022 
10023 	dprint((1, "PEDetach FAIL: %d: %s", errno, err));
10024 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err);
10025 	Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
10026 	return(TCL_ERROR);
10027     }
10028 
10029     /* package up response */
10030     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10031 			     Tcl_NewListObj(1, &tObj));
10032 
10033     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10034 			     Tcl_NewListObj(1, &stObj));
10035 
10036     rvobj = Tcl_NewLongObj(name_file_size(tfn));
10037     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10038 			     Tcl_NewListObj(1, &rvobj));
10039     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10040 			     Tcl_NewListObj(1, &fnObj));
10041     rvobj = Tcl_NewStringObj(tfn, -1);
10042     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10043 			     Tcl_NewListObj(1, &rvobj));
10044 
10045     return(TCL_OK);
10046 }
10047 
10048 
10049 /*
10050  * peAttachInfo -
10051  *
10052  * Params: argv[0] == attachment part number
10053  *
10054  * Returns: list containing:
10055  *
10056  *		0) response: OK or ERROR
10057  *	     if OK
10058  *		1) attachment's mime type
10059  *		2) attachment's mime sub-type
10060  *		3) attachment's size in bytes (decoded)
10061  *		4) attachment's given file name (if any)
10062  *		5) tmp file holding raw attachment data
10063  */
10064 int
peAttachInfo(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10065 peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10066 {
10067     char	 *part;
10068     long	  raw;
10069     BODY	 *body;
10070     PARMLIST_S	 *plist;
10071     Tcl_Obj	 *tObj, *stObj, *fnObj;
10072 
10073     if((part = Tcl_GetStringFromObj(objv[0], NULL))
10074        && (raw = peSequenceNumber(uid))
10075        && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) part))){
10076 
10077 	peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
10078     }
10079     else{
10080 	Tcl_SetResult(interp, "Can't get message data", TCL_STATIC);
10081 	return(TCL_ERROR);
10082     }
10083 
10084     /* package up response */
10085 
10086     /* filename */
10087     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj);
10088 
10089     /* type */
10090     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
10091 
10092     /* subtype */
10093     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj);
10094 
10095     /* encoding */
10096     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10097 			     Tcl_NewStringObj((body->encoding < ENCMAX)
10098 						? body_encodings[body->encoding]
10099 						: "Unknown", -1));
10100 
10101     /* parameters */
10102     if((plist = rfc2231_newparmlist(body->parameter)) != NULL){
10103 	Tcl_Obj *lObj = Tcl_NewListObj(0, NULL);
10104 	Tcl_Obj *pObj[2];
10105 
10106 	while(rfc2231_list_params(plist)){
10107 	    pObj[0] = Tcl_NewStringObj(plist->attrib, -1);
10108 	    pObj[1] = Tcl_NewStringObj(plist->value, -1);
10109 	    Tcl_ListObjAppendElement(interp, lObj,
10110 				     Tcl_NewListObj(2, pObj));
10111 	}
10112 
10113 	Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj);
10114 	rfc2231_free_parmlist(&plist);
10115     }
10116 
10117     /* size guesstimate */
10118     Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10119 			     Tcl_NewStringObj(comatose((body->encoding == ENCBASE64)
10120 					       ? ((body->size.bytes * 3)/4)
10121 					       : body->size.bytes), -1));
10122 
10123     return(TCL_OK);
10124 }
10125 
10126 
10127 /*
10128  * peSaveDefault - Default saved file name for the given message
10129  *	    specified collection/folder
10130  *
10131  * Params:
10132  *
10133  * Returns: name of saved message folder or empty string
10134  *
10135  */
10136 int
peSaveDefault(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10137 peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10138 {
10139     char      *folder;
10140     CONTEXT_S *cntxt, *cp;
10141     int	       colid;
10142     long       rawno;
10143     ENVELOPE  *env;
10144 
10145     if(uid){
10146 	if(!(env = pine_mail_fetchstructure(wps_global->mail_stream,
10147 				       rawno = peSequenceNumber(uid),
10148 				       NULL))){
10149 	    Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
10150 	    return(TCL_ERROR);
10151 	}
10152     }
10153     else
10154       env = NULL;
10155 
10156     if(!(folder = save_get_default(wps_global, env, rawno, NULL, &cntxt))){
10157 	Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE);
10158 	return(TCL_ERROR);		/* message expunged! */
10159     }
10160 
10161     for(colid = 0, cp = wps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next)
10162       ;
10163 
10164     if(!cp)
10165       colid = 0;
10166 
10167     (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10168 				    Tcl_NewIntObj(colid));
10169     (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10170 				    Tcl_NewStringObj(folder, -1));
10171     return(TCL_OK);
10172 }
10173 
10174 
10175 /*
10176  * peSaveWork - Save message with given UID in current folder to
10177  *	        specified collection/folder
10178  *
10179  * Params: argv[0] == destination context number
10180  *	   argv[1] == testination foldername
10181  *
10182  *
10183  */
10184 int
peSaveWork(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv,long sflags)10185 peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags)
10186 {
10187     int	       flgs = 0, i, colid;
10188     char      *folder, *err = NULL;
10189     CONTEXT_S *cp;
10190 
10191     if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){
10192 	if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
10193 	    mn_set_cur(sp_msgmap(wps_global->mail_stream), peMessageNumber(uid));
10194 	    for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
10195 	      if(i == colid)
10196 		break;
10197 
10198 	    if(cp){
10199 		if(!READONLY_FOLDER(wps_global->mail_stream)
10200 		   && (sflags & PSW_COPY) != PSW_COPY
10201 		   && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, wps_global)))
10202 		  flgs |= SV_DELETE;
10203 
10204 		if(colid == 0 && !strucmp(folder, "inbox"))
10205 		  flgs |= SV_INBOXWOCNTXT;
10206 
10207 		if(sflags & (PSW_COPY | PSW_MOVE))
10208 		  flgs |= SV_FIX_DELS;
10209 
10210 		i = save(wps_global, wps_global->mail_stream,
10211 			 cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
10212 
10213 		if(i == mn_total_cur(sp_msgmap(wps_global->mail_stream))){
10214 		    if(mn_total_cur(sp_msgmap(wps_global->mail_stream)) <= 1L){
10215 			if(wps_global->context_list->next
10216 			   && context_isambig(folder)){
10217 			    char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders";
10218 			    snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
10219 				     "Message %s %s to \"%.15s%s\" in <%.15s%s>",
10220 				     long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
10221 				     (sflags & PSW_MOVE) ? "moved" : "copied",
10222 				     folder,
10223 				     (strlen(folder) > 15) ? "..." : "",
10224 				     tag,
10225 				     (strlen(tag) > 15) ? "..." : "");
10226 			}
10227 			else
10228 			  snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
10229 				   "Message %s %s to folder \"%.27s%s\"",
10230 				   long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
10231 				   (sflags & PSW_MOVE) ? "moved" : "copied",
10232 				   folder,
10233 				   (strlen(folder) > 27) ? "..." : "");
10234 		    }
10235 		    else{
10236 			/* with mn_set_cur above, this *should not* happen */
10237 			Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE);
10238 			return(TCL_ERROR);
10239 		    }
10240 
10241 		    if(sflags == PSW_NONE && (flgs & SV_DELETE)){
10242 			strncat(wtmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(wtmp_20k_buf)-1);
10243 			wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
10244 		    }
10245 
10246 		    q_status_message(SM_ORDER, 0, 3, wtmp_20k_buf);
10247 		    return(TCL_OK);
10248 		}
10249 
10250 		err = wps_global->last_error;
10251 	    }
10252 	    else
10253 	      err = "open: Unrecognized collection ID";
10254 	}
10255 	else
10256 	  err = "open: Can't read folder";
10257     }
10258     else
10259       err = "open: Can't get collection ID";
10260 
10261     Tcl_SetResult(interp, err, TCL_VOLATILE);
10262     return(TCL_ERROR);
10263 }
10264 
10265 /*
10266  * peSave - Save message with given UID in current folder to
10267  *	    specified collection/folder
10268  *
10269  * Params: argv[0] == destination context number
10270  *	   argv[1] == testination foldername
10271  *
10272  * NOTE: just a wrapper around peSaveWork
10273  */
10274 int
peSave(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10275 peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10276 {
10277     return(peSaveWork(interp, uid, objc, objv, PSW_NONE));
10278 }
10279 
10280 
10281 /*
10282  * peCopy - Copy message with given UID in current folder to
10283  *	    specified collection/folder
10284  *
10285  * Params: argv[0] == destination context number
10286  *	   argv[1] == testination foldername
10287  *
10288  * NOTE: just a wrapper around peSaveWork that makes sure
10289  *       delete-on-save is NOT set
10290  */
10291 int
peCopy(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10292 peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10293 {
10294     return(peSaveWork(interp, uid, objc, objv, PSW_COPY));
10295 }
10296 
10297 
10298 /*
10299  * peMove - Move message with given UID in current folder to
10300  *	    specified collection/folder
10301  *
10302  * Params: argv[0] == destination context number
10303  *	   argv[1] == testination foldername
10304  *
10305  * NOTE: just a wrapper around peSaveWork that makes sure
10306  *       delete-on-save IS set so it can be expunged
10307  */
10308 int
peMove(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10309 peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10310 {
10311     return(peSaveWork(interp, uid, objc, objv, PSW_MOVE));
10312 }
10313 
10314 
10315 /*
10316  * peGotoDefault - Default Goto command file name for the given message
10317  *	    specified collection/folder
10318  *
10319  * Params:
10320  *
10321  * Returns: name of Goto command default folder or empty string
10322  *
10323  */
10324 int
peGotoDefault(Tcl_Interp * interp,imapuid_t uid,Tcl_Obj ** objv)10325 peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv)
10326 {
10327     char      *folder = NULL;
10328     CONTEXT_S *cntxt, *cp;
10329     int	       colid, inbox;
10330 
10331     cntxt = broach_get_folder(wps_global->context_current, &inbox, &folder);
10332 
10333     for(colid = 0, cp = wps_global->context_list; cp != cntxt ; colid++, cp = cp->next)
10334       ;
10335 
10336     if(!cp)
10337       colid = 0;
10338 
10339     (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10340 				    Tcl_NewIntObj(colid));
10341     (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10342 				    Tcl_NewStringObj(folder ? folder : "", -1));
10343     return(TCL_OK);
10344 }
10345 
10346 
10347 /*
10348  * peReplyQuote -
10349  *
10350  * Params: argv[0] == attachment part number
10351  *
10352  * Returns: list containing:
10353  *
10354  */
10355 int
peReplyQuote(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)10356 peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10357 {
10358     char     *quote;
10359     ENVELOPE *env;
10360 
10361     if(uid){
10362 	if((env = pine_mail_fetchstructure(wps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){
10363 	    quote = reply_quote_str(env);
10364 	    Tcl_SetResult(interp, quote, TCL_VOLATILE);
10365 	    fs_give((void **) &quote);
10366 	}
10367 	else{
10368 	    Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
10369 	    return(TCL_ERROR);
10370 	}
10371     }
10372     else
10373       Tcl_SetResult(interp, "> ", TCL_VOLATILE);
10374 
10375     return(TCL_OK);
10376 }
10377 
10378 
10379 void
peGetMimeTyping(BODY * body,Tcl_Obj ** tObjp,Tcl_Obj ** stObjp,Tcl_Obj ** fnObjp,Tcl_Obj ** extObjp)10380 peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp)
10381 {
10382     char *ptype = NULL, *psubtype = NULL, *pfile = NULL;
10383 
10384     /*-------  Figure out suggested file name ----*/
10385     if(body){
10386 	if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){
10387 	    /*
10388 	     * if part is generic, see if we can get anything
10389 	     * more from the suggested filename's extension...
10390 	     */
10391 	    if(body->type == TYPEAPPLICATION
10392 	       && (!body->subtype
10393 		   || !strucmp(body->subtype, "octet-stream"))){
10394 		BODY *fakebody = mail_newbody();
10395 
10396 		if(set_mime_type_by_extension(fakebody, pfile)){
10397 		    ptype = body_type_names(fakebody->type);
10398 		    psubtype = cpystr(fakebody->subtype);
10399 		}
10400 
10401 		mail_free_body(&fakebody);
10402 	    }
10403 	}
10404 
10405 	if(!ptype) {
10406 	    ptype = body_type_names(body->type);
10407 	    psubtype = cpystr(body->subtype
10408 			      ? body->subtype
10409 			      : (body->type == TYPETEXT)
10410 			      ? "plain"
10411 			      : (body->type == TYPEAPPLICATION)
10412 			      ? "octet-stream"
10413 			      : "");
10414 	}
10415     }
10416     else{
10417 	ptype = body_type_names(TYPETEXT);
10418 	psubtype = cpystr("plain");
10419     }
10420 
10421     if(extObjp){
10422 	*extObjp = Tcl_NewStringObj("", 0);
10423 
10424 	if(ptype && psubtype && pfile){
10425 	    size_t l;
10426 	    char *mtype;
10427 	    char  extbuf[32];	/* mailcap.c limits to three */
10428 
10429 	    l = strlen(ptype) + strlen(psubtype) + 1;
10430 	    mtype = (char *) fs_get((l+1) * sizeof(char));
10431 
10432 	    snprintf(mtype, l+1, "%s/%s", ptype, psubtype);
10433 
10434 	    if(!set_mime_extension_by_type(extbuf, mtype)){
10435 		char *dotp, *p;
10436 
10437 		for(dotp = NULL, p = pfile; *p; p++)
10438 		  if(*p == '.')
10439 		    dotp = p + 1;
10440 
10441 		if(dotp)
10442 		  Tcl_SetStringObj(*extObjp, dotp, -1);
10443 	    }
10444 	    else
10445 	      Tcl_SetStringObj(*extObjp, extbuf, -1);
10446 
10447 	    fs_give((void **) &mtype);
10448 	}
10449     }
10450 
10451     if(tObjp)
10452       *tObjp = Tcl_NewStringObj(ptype, -1);
10453 
10454     if(psubtype){
10455 	if(stObjp)
10456 	  *stObjp = Tcl_NewStringObj(psubtype, -1);
10457 
10458 	fs_give((void **) &psubtype);
10459     }
10460     else if(stObjp)
10461       *stObjp = Tcl_NewStringObj("", 0);
10462 
10463     if(pfile){
10464 	if(fnObjp)
10465 	  *fnObjp = Tcl_NewStringObj(pfile, -1);
10466 
10467 	fs_give((void **) &pfile);
10468     }
10469     else if(fnObjp)
10470       *fnObjp = Tcl_NewStringObj("", 0);
10471 }
10472 
10473 
10474 /*
10475  * peAppListF - generate a list of elements based on fmt string,
10476  *              then append it to the given list object
10477  *
10478  */
10479 int
peAppListF(Tcl_Interp * interp,Tcl_Obj * lobjp,char * fmt,...)10480 peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...)
10481 {
10482     va_list	   args;
10483     char	  *p, *sval, nbuf[128];
10484     int		   ival, err = 0;
10485     unsigned int   uval;
10486     long	   lval;
10487     unsigned long  luval;
10488     PATTERN_S *pval;
10489     ADDRESS   *aval;
10490     INTVL_S   *vval;
10491     Tcl_Obj   *lObj = NULL, *sObj;
10492 
10493     if((lObj = Tcl_NewListObj(0, NULL)) != NULL){
10494 	va_start(args, fmt);
10495 	for(p = fmt; *p && !err; p++){
10496 	    sObj = NULL;
10497 
10498 	    if(*p == '%')
10499 	      switch(*++p){
10500 		case 'i' :	/* int value */
10501 		  ival = va_arg(args, int);
10502 		  if((sObj = Tcl_NewIntObj(ival)) == NULL)
10503 		    err++;
10504 
10505 		  break;
10506 
10507 		case 'u' :	/* unsigned int value */
10508 		  uval = va_arg(args, unsigned int);
10509 		  snprintf(nbuf, sizeof(nbuf), "%u", uval);
10510 		  if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10511 		    err++;
10512 
10513 		  break;
10514 
10515 		case 'l' :	/* long value */
10516 		  if(*(p+1) == 'u'){
10517 		      p++;
10518 		      luval = va_arg(args, unsigned long);
10519 		      snprintf(nbuf, sizeof(nbuf), "%lu", luval);
10520 		      if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10521 			err++;
10522 		  }
10523 		  else{
10524 		      lval = va_arg(args, long);
10525 		      if((sObj = Tcl_NewLongObj(lval)) == NULL)
10526 			err++;
10527 		  }
10528 
10529 		  break;
10530 
10531 		case 's' :	/* string value */
10532 		  sval = va_arg(args, char *);
10533 		  sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10534 		  if(sObj == NULL)
10535 		    err++;
10536 
10537 		  break;
10538 
10539 		case 'a':	/* ADDRESS list */
10540 		  aval = va_arg(args, ADDRESS *);
10541 		  if(aval){
10542 		      char	   *tmp, *p;
10543 		      RFC822BUFFER  rbuf;
10544 		      size_t	    len;
10545 
10546 		      len = est_size(aval);
10547 		      tmp = (char *) fs_get(len * sizeof(char));
10548 		      tmp[0] = '\0';
10549 		      rbuf.f   = dummy_soutr;
10550 		      rbuf.s   = NULL;
10551 		      rbuf.beg = tmp;
10552 		      rbuf.cur = tmp;
10553 		      rbuf.end = tmp+len-1;
10554 		      rfc822_output_address_list(&rbuf, aval, 0L, NULL);
10555 		      *rbuf.cur = '\0';
10556 		      p	   = (char *) rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf, SIZEOF_20KBUF, tmp);
10557 		      sObj = Tcl_NewStringObj(p, strlen(p));
10558 		      fs_give((void **) &tmp);
10559 		  }
10560 		  else
10561 		    sObj = Tcl_NewStringObj("", -1);
10562 
10563 		  break;
10564 
10565 		case 'p':	/* PATTERN_S * */
10566 		  pval = va_arg(args, PATTERN_S *);
10567 		  sval = pattern_to_string(pval);
10568 		  sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10569 		  break;
10570 
10571 		case 'v':	/* INTVL_S * */
10572 		  vval = va_arg(args, INTVL_S *);
10573 		  if(vval){
10574 		      for(; vval != NULL; vval = vval->next){
10575 			  peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax);
10576 		      }
10577 		  }
10578 		  else
10579 		    sObj = Tcl_NewListObj(0, NULL);
10580 
10581 		  break;
10582 
10583 		case 'o':	/* Tcl_Obj * */
10584 		  sObj = va_arg(args, Tcl_Obj *);
10585 		  break;
10586 	      }
10587 
10588 	    if(sObj)
10589 	      Tcl_ListObjAppendElement(interp, lObj, sObj);
10590 	}
10591 
10592 	va_end(args);
10593     }
10594 
10595     return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR);
10596 }
10597 
10598 /*
10599  * pePatAppendID - append list of pattern identity variables to given object
10600  */
10601 void
pePatAppendID(Tcl_Interp * interp,Tcl_Obj * patObj,PAT_S * pat)10602 pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10603 {
10604     Tcl_Obj  *resObj;
10605 
10606     resObj = Tcl_NewListObj(0, NULL);
10607     peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick);
10608     peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment);
10609     Tcl_ListObjAppendElement(interp, patObj, resObj);
10610 }
10611 
10612 
10613 /*
10614  * pePatAppendPattern - append list of pattern variables to given object
10615  */
10616 void
pePatAppendPattern(Tcl_Interp * interp,Tcl_Obj * patObj,PAT_S * pat)10617 pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10618 {
10619     ARBHDR_S *ah;
10620     Tcl_Obj  *resObj;
10621 
10622     resObj = Tcl_NewListObj(0, NULL);
10623     peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to);
10624     peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from);
10625     peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender);
10626     peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc);
10627     peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip);
10628     peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic);
10629     peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news);
10630     peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj);
10631     peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext);
10632     peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext);
10633     peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword);
10634     peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets);
10635 
10636     peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score);
10637     peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age);
10638     peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size);
10639 
10640     if((ah = pat->patgrp->arbhdr) != NULL){
10641 	Tcl_Obj *hlObj, *hObj;
10642 
10643 	hlObj = Tcl_NewListObj(0, NULL);
10644 	Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1));
10645 
10646 	for(; ah; ah = ah->next){
10647 	    hObj = Tcl_NewListObj(0, NULL);
10648 	    peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p);
10649 	    Tcl_ListObjAppendElement(interp, hlObj, hObj);
10650 	}
10651 
10652 	Tcl_ListObjAppendElement(interp, resObj, hlObj);
10653     }
10654 
10655     switch(pat->patgrp->fldr_type){
10656       case FLDR_ANY:
10657 	peAppListF(interp, resObj, "%s%s", "ftype", "any");
10658 	break;
10659       case FLDR_NEWS:
10660 	peAppListF(interp, resObj, "%s%s", "ftype", "news");
10661 	break;
10662       case FLDR_EMAIL:
10663 	peAppListF(interp, resObj, "%s%s", "ftype", "email");
10664 	break;
10665       case FLDR_SPECIFIC:
10666 	peAppListF(interp, resObj, "%s%s", "ftype", "specific");
10667 	break;
10668     }
10669 
10670     peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder);
10671     peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new));
10672     peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec));
10673     peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del));
10674     peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp));
10675     peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans));
10676     peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj));
10677     peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom));
10678     peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy));
10679 
10680     Tcl_ListObjAppendElement(interp, patObj, resObj);
10681 }
10682 
10683 
10684 char *
pePatStatStr(int value)10685 pePatStatStr(int value)
10686 {
10687     switch(value){
10688       case PAT_STAT_EITHER:
10689 	return("either");
10690 	break;
10691 
10692       case PAT_STAT_YES:
10693 	return("yes");
10694 	break;
10695 
10696       default :
10697 	return("no");
10698 	break;
10699     }
10700 }
10701 
10702 
10703 /*
10704  * peCreateUserContext - create new wps_global and set it up
10705  */
10706 char *
peCreateUserContext(Tcl_Interp * interp,char * user,char * config,char * defconf)10707 peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf)
10708 {
10709     if(wps_global)
10710       peDestroyUserContext(&wps_global);
10711 
10712     set_collation(1, 1);
10713 
10714     wps_global = new_pine_struct();
10715 
10716     /*----------------------------------------------------------------------
10717            Place any necessary constraints on pith processing
10718       ----------------------------------------------------------------------*/
10719 
10720     /* got thru close procedure without expunging */
10721     wps_global->noexpunge_on_close = 1;
10722 
10723     /* do NOT let user set path to local executable */
10724     wps_global->vars[V_SENDMAIL_PATH].is_user = 0;
10725 
10726 
10727     /*----------------------------------------------------------------------
10728            Proceed with reading acquiring user settings
10729       ----------------------------------------------------------------------*/
10730     if(wps_global->pinerc)
10731       fs_give((void **) &wps_global->pinerc);
10732 
10733     if(wps_global->prc)
10734       free_pinerc_s(&wps_global->prc);
10735 
10736     if(!IS_REMOTE(config)){
10737 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config);
10738 	return(wtmp_20k_buf);
10739     }
10740 
10741     wps_global->prc = new_pinerc_s(config);
10742 
10743     if(defconf){
10744 	if(wps_global->pconf)
10745 	  free_pinerc_s(&wps_global->pconf);
10746 
10747 	wps_global->pconf = new_pinerc_s(defconf);
10748     }
10749 
10750     /*
10751      * Fake up some user information
10752      */
10753     wps_global->ui.login = cpystr(user);
10754 
10755 #ifdef	DEBUG
10756     /*
10757      * Prep for IMAP debugging
10758      */
10759     setup_imap_debug();
10760 #endif
10761 
10762     /* CHECK FOR AND PASS BACK ANY INIT ERRORS */
10763     return(peLoadConfig(wps_global));
10764 }
10765 
10766 
10767 
10768 void
peDestroyUserContext(struct pine ** pps)10769 peDestroyUserContext(struct pine **pps)
10770 {
10771 
10772     completely_done_with_adrbks();
10773 
10774     free_pinerc_strings(pps);
10775 #if	0
10776     imap_flush_passwd_cache(TRUE);
10777 #endif
10778     clear_index_cache(sp_inbox_stream(), 0);
10779     free_newsgrp_cache();
10780     mailcap_free();
10781     close_patterns(0L);
10782     free_extra_hdrs();
10783     free_contexts(&wps_global->context_list);
10784 
10785     pico_endcolor();
10786 
10787     free_strlist(&peCertHosts);
10788 
10789     free_pine_struct(pps);
10790 }
10791 
10792 
10793 char *
peLoadConfig(struct pine * pine_state)10794 peLoadConfig(struct pine *pine_state)
10795 {
10796     int   rv;
10797     char *s, *db = NULL;
10798     extern void init_signals(void);	/* in signal.c */
10799 
10800     if(!pine_state)
10801       return("No global state present");
10802 
10803 #if	0
10804 /*turned off because we don't care about local user*/
10805     /* need home directory early */
10806     get_user_info(&pine_state->ui);
10807 
10808     pine_state->home_dir = cpystr((getenv("HOME") != NULL)
10809 				    ? getenv("HOME")
10810 				    : pine_state->ui.homedir);
10811 #endif
10812 
10813     init_pinerc(pine_state, &db);
10814 
10815     fs_give((void **) &db);
10816 
10817     /*
10818      * Initial allocation of array of stream pool pointers.
10819      * We do this before init_vars so that we can re-use streams used for
10820      * remote config files. These sizes may get changed later.
10821      */
10822     wps_global->s_pool.max_remstream  = 2;
10823 
10824     wps_global->c_client_error[0] = '\0';
10825 
10826     pePrepareForAuthException();
10827 
10828     peInitVars(pine_state);
10829 
10830     if((s = peAuthException()) != NULL)
10831       return(s);
10832     else if(wps_global->c_client_error[0])
10833       return(wps_global->c_client_error);
10834 
10835     mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
10836     mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
10837     mail_parameters(NULL, SET_FREEELTSPAREP,    (void *) free_pine_elt);
10838 
10839     /*
10840      * Install callback to handle certificate validation failures,
10841      * allowing the user to continue if they wish.
10842      */
10843     mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery);
10844     mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure);
10845 
10846     /*
10847      * Set up a c-client read timeout and timeout handler.  In general,
10848      * it shouldn't happen, but a server crash or dead link can cause
10849      * pine to appear wedged if we don't set this up...
10850      */
10851     mail_parameters(NULL, SET_OPENTIMEOUT,
10852 		    (void *)((pine_state->VAR_TCPOPENTIMEO
10853 			      && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
10854 			       ? (long) rv : 30L));
10855     mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout);
10856 
10857     if(pine_state->VAR_RSHOPENTIMEO
10858 	&& ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
10859       mail_parameters(NULL, SET_RSHTIMEOUT, (void *) (long) rv);
10860 
10861     if(pine_state->VAR_SSHOPENTIMEO
10862 	&& ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4))
10863       mail_parameters(NULL, SET_SSHTIMEOUT, (void *) (long) rv);
10864 
10865     /*
10866      * Tell c-client not to be so aggressive about uid mappings
10867      */
10868     mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
10869 
10870     /*
10871      * Setup referral handling
10872      */
10873     mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
10874     mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
10875 
10876     /*
10877      * Install extra headers to fetch along with all the other stuff
10878      * mail_fetch_structure and mail_fetch_overview requests.
10879      */
10880     calc_extra_hdrs();
10881 
10882     if(get_extra_hdrs())
10883       (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs());
10884 
10885     (void) init_username(pine_state);
10886 
10887     (void) init_hostname(wps_global);
10888 
10889 #ifdef ENABLE_LDAP
10890     (void) init_ldap_pname(wps_global);
10891 #endif /* ENABLE_LDAP */
10892 
10893     if(wps_global->prc && wps_global->prc->type == Loc &&
10894        can_access(wps_global->pinerc, ACCESS_EXISTS) == 0 &&
10895        can_access(wps_global->pinerc, EDIT_ACCESS) != 0)
10896       wps_global->readonly_pinerc = 1;
10897 
10898     /*
10899      * c-client needs USR2 and we might as well
10900      * do something sensible with HUP and TERM
10901      */
10902     init_signals();
10903 
10904     strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name));
10905     pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0';
10906 
10907     init_folders(pine_state);		/* digest folder spec's */
10908 
10909     /*
10910      * Various options we want to make sure are set OUR way
10911      */
10912     F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state);
10913     F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state);
10914     F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state);
10915 
10916     /*
10917      * Fake screen dimensions for index formatting and
10918      * message display wrap...
10919      */
10920     wps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo));
10921     wps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH;
10922     wps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH;
10923     if(wps_global->VAR_WP_COLUMNS){
10924 	int w = atoi(wps_global->VAR_WP_COLUMNS);
10925 	if(w >= 20 && w <= 128)
10926 	  wps_global->ttyo->screen_cols = w;
10927     }
10928 
10929     wps_global->ttyo->header_rows = 0;
10930     wps_global->ttyo->footer_rows = 0;
10931 
10932 
10933     /* init colors */
10934     if(wps_global->VAR_NORM_FORE_COLOR)
10935       pico_nfcolor(wps_global->VAR_NORM_FORE_COLOR);
10936 
10937     if(wps_global->VAR_NORM_BACK_COLOR)
10938       pico_nbcolor(wps_global->VAR_NORM_BACK_COLOR);
10939 
10940     if(wps_global->VAR_REV_FORE_COLOR)
10941       pico_rfcolor(wps_global->VAR_REV_FORE_COLOR);
10942 
10943     if(wps_global->VAR_REV_BACK_COLOR)
10944       pico_rbcolor(wps_global->VAR_REV_BACK_COLOR);
10945 
10946     pico_set_normal_color();
10947 
10948     return(NULL);
10949 }
10950 
10951 
10952 int
peCreateStream(Tcl_Interp * interp,CONTEXT_S * context,char * mailbox,int do_inbox)10953 peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox)
10954 {
10955     unsigned long  flgs = 0L;
10956     char	  *s;
10957 
10958     wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
10959 
10960     pePrepareForAuthException();
10961 
10962     if(do_inbox)
10963       flgs |= DB_INBOXWOCNTXT;
10964 
10965     if(do_broach_folder(mailbox, context, NULL, flgs) && wps_global->mail_stream){
10966 	dprint((SYSDBG_INFO, "Mailbox open: %s",
10967 		wps_global->mail_stream->mailbox ? wps_global->mail_stream->mailbox : "<UNKNOWN>"));
10968 	return(TCL_OK);
10969     }
10970 
10971     Tcl_SetResult(interp,
10972 		  (s = peAuthException())
10973 		    ? s
10974 		    : (*wps_global->last_error)
10975 		       ? wps_global->last_error
10976 		       : "Login Error",
10977 		  TCL_VOLATILE);
10978     return(TCL_ERROR);
10979 }
10980 
10981 
10982 void
peDestroyStream(struct pine * ps)10983 peDestroyStream(struct pine *ps)
10984 {
10985     int cur_is_inbox;
10986 
10987     if(ps){
10988 	cur_is_inbox = (sp_inbox_stream() == wps_global->mail_stream);
10989 
10990 	/* clean up open streams */
10991 	if(ps->mail_stream){
10992 	    expunge_and_close(ps->mail_stream, NULL, EC_NONE);
10993 	    wps_global->mail_stream = NULL;
10994 	    wps_global->cur_folder[0] = '\0';
10995 	}
10996 
10997 	if(ps->msgmap)
10998 	  mn_give(&ps->msgmap);
10999 
11000 	if(sp_inbox_stream() && !cur_is_inbox){
11001 	    ps->mail_stream   = sp_inbox_stream();
11002 	    ps->msgmap	      = sp_msgmap(ps->mail_stream);
11003 	    sp_set_expunge_count(wps_global->mail_stream, 0L);
11004 	    expunge_and_close(sp_inbox_stream(), NULL, EC_NONE);
11005 	    mn_give(&ps->msgmap);
11006 	}
11007     }
11008 }
11009 
11010 
11011 /*
11012  * pePrepareForAuthException - set globals to get feedback from bowels of c-client
11013  */
11014 void
pePrepareForAuthException(void)11015 pePrepareForAuthException(void)
11016 {
11017     peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0;
11018 }
11019 
11020 /*
11021  * pePrepareForAuthException - check globals getting feedback from bowels of c-client
11022  */
11023 char *
peAuthException()11024 peAuthException()
11025 {
11026     static char buf[CRED_REQ_SIZE];
11027 
11028     if(peCertQuery){
11029 	snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor);
11030 	return(buf);
11031     }
11032 
11033     if(peCertFailure){
11034 	snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor);
11035 	return(buf);
11036     }
11037 
11038     if(peNoPassword){
11039 	snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor);
11040 	return(buf);
11041     }
11042 
11043     if(peCredentialError){
11044 	snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor);
11045 	return(buf);
11046     }
11047 
11048     return(NULL);
11049 }
11050 
11051 
11052 void
peInitVars(struct pine * ps)11053 peInitVars(struct pine *ps)
11054 {
11055     init_vars(ps, NULL);
11056 
11057     /*
11058      * fix display/keyboard-character-set to utf-8
11059      *
11060      *
11061      */
11062 
11063     if(ps->display_charmap)
11064       fs_give((void **) &ps->display_charmap);
11065 
11066     ps->display_charmap = cpystr(WP_INTERNAL_CHARSET);
11067 
11068     if(ps->keyboard_charmap)
11069       fs_give((void **) &ps->keyboard_charmap);
11070 
11071     ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET);
11072 
11073     (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);;
11074 }
11075 
11076 
11077 
11078 int
peMessageBounce(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)11079 peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
11080 {
11081     char      *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1];
11082     long       rawno;
11083     ENVELOPE  *env, *outgoing = NULL;
11084     METAENV   *metaenv;
11085     PINEFIELD *custom;
11086     BODY      *body = NULL;
11087 
11088     if(uid){
11089 	rawno = peSequenceNumber(uid);
11090 
11091 	if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){
11092 	    if(objc == 2 && objv[1]){
11093 		subj = Tcl_GetStringFromObj(objv[1], NULL);
11094 	    }
11095 	    else if((env = mail_fetchstructure(wps_global->mail_stream, rawno, NULL)) != NULL){
11096 		subj = env->subject;
11097 	    }
11098 	    else{
11099 		Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
11100 		return(TCL_ERROR);
11101 	    }
11102 
11103 	    if((errstr = bounce_msg_body(wps_global->mail_stream, rawno, NULL,
11104 					 &to, subj, &outgoing, &body, NULL))){
11105 		Tcl_SetResult(interp, errstr, TCL_VOLATILE);
11106 		return(TCL_ERROR);
11107 	    }
11108 
11109 	    metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11110 
11111 	    if(!outgoing->from)
11112 	      outgoing->from = generate_from();
11113 
11114 	    rfc822_date(wtmp_20k_buf);
11115 	    outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
11116 
11117 	    outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11118 	    if(!outgoing->message_id)
11119 	      outgoing->message_id = generate_message_id(NULL);
11120 
11121 	    /* NO FCC  */
11122 
11123 	    if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11124 	      errstr = errbuf;
11125 
11126 	    pine_free_body(&body);
11127 
11128 	    pine_free_env(&metaenv);
11129 
11130 	    if(custom)
11131 	      free_customs(custom);
11132 
11133 	    mail_free_envelope(&outgoing);
11134 	    pine_free_body(&body);
11135 
11136 	}
11137     }
11138 
11139     Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE);
11140     return(errstr ? TCL_ERROR : TCL_OK);
11141 }
11142 
11143 
11144 int
peMessageSpamNotice(interp,uid,objc,objv)11145 peMessageSpamNotice(interp, uid, objc, objv)
11146     Tcl_Interp	*interp;
11147     imapuid_t	 uid;
11148     int		 objc;
11149     Tcl_Obj    **objv;
11150 {
11151     char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
11152     long  rawno;
11153 
11154     if(uid){
11155 	rawno = peSequenceNumber(uid);
11156 
11157 	if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){
11158 
11159 	    if(objv[1])
11160 	      subj = Tcl_GetStringFromObj(objv[1], NULL);
11161 
11162 	    rs = peSendSpamReport(rawno, to, subj, errbuf);
11163 	}
11164     }
11165 
11166     Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE);
11167     return(rs ? TCL_ERROR : TCL_OK);
11168 }
11169 
11170 
11171 char *
peSendSpamReport(long rawno,char * to,char * subj,char * errbuf)11172 peSendSpamReport(long rawno, char *to, char *subj, char *errbuf)
11173 {
11174     char	*errstr = NULL, *tmp_a_string;
11175     ENVELOPE	*env, *outgoing;
11176     METAENV	*metaenv;
11177     PINEFIELD	*custom;
11178     BODY	*body;
11179     static char	*fakedomain = "@";
11180     void	*msgtext;
11181 
11182 
11183     if((env = mail_fetchstructure(wps_global->mail_stream, rawno, NULL)) == NULL){
11184 	return(wps_global->last_error);
11185     }
11186 
11187     /* empty subject gets "spam" subject */
11188     if(!(subj && *subj))
11189       subj = env->subject;
11190 
11191     /*---- New Body to start with ----*/
11192     body       = mail_newbody();
11193     body->type = TYPEMULTIPART;
11194 
11195     /*---- The TEXT part/body ----*/
11196     body->nested.part            = mail_newbody_part();
11197     body->nested.part->body.type = TYPETEXT;
11198 
11199     if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
11200 	pine_free_body(&body);
11201 	return("peSendSpamReport: Can't allocate text");
11202     }
11203     else{
11204 	sprintf(wtmp_20k_buf,
11205 		"The attached message is being reported to <%s> as Spam\n",
11206 		to);
11207 	so_puts((STORE_S *) msgtext, wtmp_20k_buf);
11208 	body->nested.part->body.contents.text.data = msgtext;
11209     }
11210 
11211     /*---- Attach the raw message ----*/
11212     if(forward_mime_msg(wps_global->mail_stream, rawno, NULL, env,
11213 			&(body->nested.part->next), msgtext)){
11214 	outgoing = mail_newenvelope();
11215 	metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11216     }
11217     else{
11218 	pine_free_body(&body);
11219 	return("peSendSpamReport: Can't generate forwarded message");
11220     }
11221 
11222     /* rfc822_parse_adrlist feels free to destroy input so copy */
11223     tmp_a_string = cpystr(to);
11224     rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
11225 			 (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
11226 			 ? fakedomain : wps_global->maildomain);
11227     fs_give((void **) &tmp_a_string);
11228 
11229     outgoing->from	  = generate_from();
11230     outgoing->subject	  = cpystr(subj);
11231     outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11232     outgoing->message_id  = generate_message_id(NULL);
11233 
11234     rfc822_date(wtmp_20k_buf);
11235     outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
11236 
11237     /* NO FCC for Spam Reporting  */
11238 
11239     if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11240       errstr = errbuf;
11241 
11242     pine_free_body(&body);
11243 
11244     pine_free_env(&metaenv);
11245 
11246     if(custom)
11247       free_customs(custom);
11248 
11249     mail_free_envelope(&outgoing);
11250     pine_free_body(&body);
11251 
11252     return(errstr);
11253 }
11254 
11255 
11256 /* * * * * * * * * * * * *  Start of Composer Routines * * * * * * * * * * * */
11257 
11258 
11259 /*
11260  * PEComposeCmd - export various bits of alpine state
11261  */
11262 int
PEComposeCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])11263 PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11264 {
11265     char *err = "PECompose: Unknown request";
11266 
11267     dprint((2, "PEComposeCmd"));
11268 
11269     if(objc == 1){
11270 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11271     }
11272     else if(!wps_global){
11273 	Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC);
11274 	return(TCL_ERROR);
11275     }
11276     else{
11277 	char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11278 
11279 	if(s1){
11280 	    if(!strcmp(s1, "post")){
11281 		long flags = PMC_NONE;
11282 		if(F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
11283 		  flags |= PMC_FORCE_QUAL;
11284 
11285 		return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags));
11286 	    }
11287 	    else if(objc == 2){
11288 		if(!strcmp(s1, "userhdrs")){
11289 		    int	       i;
11290 		    char      *p;
11291 		    PINEFIELD *custom, *cp;
11292 		    ADDRESS   *from;
11293 		    static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL};
11294 
11295 		    custom = peCustomHdrs();
11296 
11297 		    for(i = 0; standard[i]; i++){
11298 			p = NULL;
11299 			for(cp = custom; cp; cp = cp->next)
11300 			  if(!strucmp(cp->name, standard[i]))
11301 			    p = cp->textbuf;
11302 
11303 			peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p);
11304 		    }
11305 
11306 		    for(cp = custom; cp != NULL; cp = cp->next){
11307 			if(!strucmp(cp->name, "from")){
11308 			    if(F_OFF(F_ALLOW_CHANGING_FROM, wps_global))
11309 			      continue;
11310 
11311 			    if(cp->textbuf && strlen(cp->textbuf)){
11312 				p = cp->textbuf;
11313 			    }
11314 			    else{
11315 				RFC822BUFFER rbuf;
11316 
11317 				wtmp_20k_buf[0] = '\0';
11318 				rbuf.f   = dummy_soutr;
11319 				rbuf.s   = NULL;
11320 				rbuf.beg = wtmp_20k_buf;
11321 				rbuf.cur = wtmp_20k_buf;
11322 				rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
11323 				rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL);
11324 				*rbuf.cur = '\0';
11325 				mail_free_address(&from);
11326 				p = wtmp_20k_buf;
11327 			    }
11328 			}
11329 			else{
11330 			    p = cp->textbuf;
11331 			    for(i = 0; standard[i]; i++)
11332 			      if(!strucmp(standard[i], cp->name))
11333 				p = NULL;
11334 
11335 			    if(!p)
11336 			      continue;
11337 			}
11338 
11339 			peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p);
11340 		    }
11341 
11342 		    if(custom)
11343 		      free_customs(custom);
11344 
11345 		    return(TCL_OK);
11346 		}
11347 		else if(!strcmp(s1, "syshdrs")){
11348 		    int	     i;
11349 		    static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL};
11350 
11351 		    for(i = 0; extras[i]; i++)
11352 		      peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL);
11353 
11354 		    return(TCL_OK);
11355 		}
11356 		else if(!strcmp(s1, "composehdrs")){
11357 		    char **p, *q;
11358 
11359 		    if((p = wps_global->VAR_COMP_HDRS) && *p){
11360 			for(; *p; p++)
11361 			  Tcl_ListObjAppendElement(interp,
11362 						   Tcl_GetObjResult(interp),
11363 						   Tcl_NewStringObj(*p, (q = strchr(*p, ':'))
11364 									   ? (q - *p) : -1));
11365 		    }
11366 		    else
11367 		      Tcl_SetResult(interp, "", TCL_STATIC);
11368 
11369 		    return(TCL_OK);
11370 		}
11371 		else if(!strcmp(s1, "fccdefault")){
11372 		    int	       ci = 0;
11373 		    CONTEXT_S *c = default_save_context(wps_global->context_list), *c2;
11374 
11375 		    for(c2 = wps_global->context_list; c && c != c2; c2 = c2->next)
11376 		      ci++;
11377 
11378 		    Tcl_ListObjAppendElement(interp,
11379 					     Tcl_GetObjResult(interp),
11380 					     Tcl_NewIntObj(ci));
11381 		    Tcl_ListObjAppendElement(interp,
11382 					     Tcl_GetObjResult(interp),
11383 					     Tcl_NewStringObj(wps_global->VAR_DEFAULT_FCC
11384 							        ? wps_global->VAR_DEFAULT_FCC
11385 								: "", -1));
11386 		    return(TCL_OK);
11387 		}
11388 		else if(!strcmp(s1, "noattach")){
11389 		    peFreeAttach(&peCompAttach);
11390 		    Tcl_SetResult(interp, "OK", TCL_VOLATILE);
11391 		    return(TCL_OK);
11392 		}
11393 		else if(!strcmp(s1, "from")){
11394 		    RFC822BUFFER rbuf;
11395 		    ADDRESS *from = generate_from();
11396 		    wtmp_20k_buf[0] = '\0';
11397 		    rbuf.f   = dummy_soutr;
11398 		    rbuf.s   = NULL;
11399 		    rbuf.beg = wtmp_20k_buf;
11400 		    rbuf.cur = wtmp_20k_buf;
11401 		    rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
11402 		    rfc822_output_address_list(&rbuf, from, 0L, NULL);
11403 		    *rbuf.cur = '\0';
11404 		    mail_free_address(&from);
11405 		    Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
11406 		    return(TCL_OK);
11407 		}
11408 		else if(!strcmp(s1, "attachments")){
11409 		    COMPATT_S *p;
11410 		    Tcl_Obj *objAttach;
11411 
11412 		    for(p = peCompAttach; p; p = p->next)
11413 		      if(p->file){
11414 			  objAttach = Tcl_NewListObj(0, NULL);
11415 
11416 			  /* id */
11417 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11418 
11419 			  /* file name */
11420 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1));
11421 
11422 			  /* file size */
11423 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size));
11424 
11425 			  /* type/subtype */
11426 			  snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype);
11427 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(wtmp_20k_buf,-1));
11428 
11429 			  /* append to list */
11430 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11431 		      }
11432 		      else if(p->body){
11433 			  char *name;
11434 
11435 			  objAttach = Tcl_NewListObj(0, NULL);
11436 
11437 			  /* id */
11438 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11439 
11440 			  /* file name */
11441 			  if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){
11442 			      Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1));
11443 			      fs_give((void **) &name);
11444 			  }
11445 			  else
11446 			    Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1));
11447 
11448 			  /* file size */
11449 			  Tcl_ListObjAppendElement(interp, objAttach,
11450 						   Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64)
11451 								  ? ((p->l.b.body->size.bytes * 3)/4)
11452 								  : p->l.b.body->size.bytes));
11453 
11454 			  /* type/subtype */
11455 			  snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11456 				   body_type_names(p->l.b.body->type),
11457 				   p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown");
11458 			  Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(wtmp_20k_buf, -1));
11459 
11460 			  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11461 		      }
11462 
11463 		    return(TCL_OK);
11464 		}
11465 	    }
11466 	    else if(objc == 3){
11467 		if(!strcmp(s1, "unattach")){
11468 		    char *id;
11469 
11470 		    if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11471 			if(peClearAttachID(id)){
11472 			    Tcl_SetResult(interp, "OK", TCL_STATIC);
11473 			    return(TCL_OK);
11474 			}
11475 			else
11476 			  err = "Can't access attachment id";
11477 		    }
11478 		    else
11479 		      err = "Can't read attachment id";
11480 		}
11481 		else if(!strcmp(s1, "attachinfo")){
11482 		    COMPATT_S *a;
11483 		    char      *id, *s;
11484 
11485 		    /* return: remote-filename size "type/subtype" */
11486 		    if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11487 			if((a = peGetAttachID(id)) != NULL){
11488 			    if(a->file){
11489 				/* file name */
11490 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1));
11491 
11492 				/* file size */
11493 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size));
11494 
11495 				/* type/subtype */
11496 				snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype);
11497 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf,-1));
11498 
11499 				/* description */
11500 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11501 							 Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1));
11502 				return(TCL_OK);
11503 			    }
11504 			    else if(a->body){
11505 				char *name;
11506 
11507 				/* file name */
11508 				if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){
11509 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1));
11510 				    fs_give((void **) &name);
11511 				}
11512 				else
11513 				  Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1));
11514 
11515 				/* file size */
11516 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11517 							 Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64)
11518 									? ((a->l.b.body->size.bytes * 3)/4)
11519 									: a->l.b.body->size.bytes));
11520 
11521 				/* type/subtype */
11522 				snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11523 					body_type_names(a->l.b.body->type),
11524 					a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown");
11525 
11526 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf, -1));
11527 
11528 				/* description */
11529 				if(a->l.b.body->description){
11530 				    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description);
11531 				}
11532 				else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){
11533 				    snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s);
11534 				    fs_give((void **) &s);
11535 				}
11536 				else
11537 				  wtmp_20k_buf[0] = '\0';
11538 
11539 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf, -1));
11540 
11541 				return(TCL_OK);
11542 			    }
11543 
11544 			    err = "Unknown attachment type";
11545 			}
11546 			else
11547 			  err = "Can't access attachment id";
11548 		    }
11549 		    else
11550 		      err = "Can't read attachment id";
11551 		}
11552 	    }
11553 	    else if(objc == 7){
11554 		if(!strcmp(s1, "attach")){
11555 		    char *file, *remote, *type, *subtype, *desc;
11556 
11557 		    if((file = Tcl_GetStringFromObj(objv[2], NULL))
11558 		       && (type = Tcl_GetStringFromObj(objv[3], NULL))
11559 		       && (subtype = Tcl_GetStringFromObj(objv[4], NULL))
11560 		       && (remote = Tcl_GetStringFromObj(objv[5], NULL))){
11561 			int  dl;
11562 
11563 			desc = Tcl_GetStringFromObj(objv[6], &dl);
11564 
11565 			if(desc){
11566 			    Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE);
11567 			    return(TCL_OK);
11568 			}
11569 			else
11570 			  err = "Can't read file description";
11571 		    }
11572 		    else
11573 		      err = "Can't read file name";
11574 		}
11575 	    }
11576 	}
11577     }
11578 
11579     Tcl_SetResult(interp, err, TCL_STATIC);
11580     return(TCL_ERROR);
11581 }
11582 
11583 
11584 COMPATT_S *
peNewAttach(void)11585 peNewAttach(void)
11586 {
11587     COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S));
11588     memset(p, 0, sizeof(COMPATT_S));
11589     return(p);
11590 }
11591 
11592 
11593 void
peFreeAttach(COMPATT_S ** a)11594 peFreeAttach(COMPATT_S **a)
11595 {
11596     if(a && *a){
11597 	fs_give((void **) &(*a)->id);
11598 
11599 	if((*a)->file){
11600 	    if((*a)->l.f.type)
11601 	      fs_give((void **) &(*a)->l.f.type);
11602 
11603 	    if((*a)->l.f.subtype)
11604 	      fs_give((void **) &(*a)->l.f.subtype);
11605 
11606 	    if((*a)->l.f.remote)
11607 	      fs_give((void **) &(*a)->l.f.remote);
11608 
11609 	    if((*a)->l.f.local){
11610 		(void) unlink((*a)->l.f.local);
11611 		fs_give((void **) &(*a)->l.f.local);
11612 	    }
11613 
11614 	    if((*a)->l.f.description)
11615 	      fs_give((void **) &(*a)->l.f.description);
11616 	}
11617 	else if((*a)->body){
11618 	    pine_free_body(&(*a)->l.b.body);
11619 	}
11620 
11621 	peFreeAttach(&(*a)->next);
11622 	fs_give((void **) a);
11623     }
11624 }
11625 
11626 
11627 char *
peFileAttachID(char * f,char * t,char * st,char * r,char * d,int dl)11628 peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl)
11629 {
11630     COMPATT_S *ap = peNewAttach(), *p;
11631     long       hval;
11632 
11633     ap->file = TRUE;
11634     ap->l.f.local = cpystr(f);
11635     ap->l.f.size = name_file_size(f);
11636 
11637     hval = line_hash(f);
11638     while(1)			/* collisions? */
11639       if(peGetAttachID(ap->id = cpystr(long2string(hval)))){
11640 	  fs_give((void **) &ap->id);
11641 	  hval += 1;
11642       }
11643       else
11644 	break;
11645 
11646     ap->l.f.remote = cpystr(r ? r : "");
11647     ap->l.f.type = cpystr(t ? t : "Text");
11648     ap->l.f.subtype = cpystr(st ? st : "Plain");
11649 
11650     ap->l.f.description = fs_get(dl + 1);
11651     snprintf(ap->l.f.description, dl + 1, "%s", d);
11652 
11653     if((p = peCompAttach) != NULL){
11654 	do
11655 	  if(!p->next){
11656 	      p->next = ap;
11657 	      break;
11658 	  }
11659 	while((p = p->next) != NULL);
11660     }
11661     else
11662       peCompAttach = ap;
11663 
11664     return(ap->id);
11665 }
11666 
11667 
11668 char *
peBodyAttachID(BODY * b)11669 peBodyAttachID(BODY *b)
11670 {
11671     COMPATT_S *ap = peNewAttach(), *p;
11672     unsigned long hval;
11673 
11674     ap->body = TRUE;
11675     ap->l.b.body = copy_body(NULL, b);
11676 
11677     hval = b->id ? line_hash(b->id) : time(0);
11678     while(1)			/* collisions? */
11679       if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){
11680 	  fs_give((void **) &ap->id);
11681 	  hval += 1;
11682       }
11683       else
11684 	break;
11685 
11686     /* move contents pointer to copy */
11687     peBodyMoveContents(b, ap->l.b.body);
11688 
11689     if((p = peCompAttach) != NULL){
11690 	do
11691 	  if(!p->next){
11692 	      p->next = ap;
11693 	      break;
11694 	  }
11695 	while((p = p->next) != NULL);
11696     }
11697     else
11698       peCompAttach = ap;
11699 
11700     return(ap->id);
11701 }
11702 
11703 
11704 void
peBodyMoveContents(BODY * bs,BODY * bd)11705 peBodyMoveContents(BODY *bs, BODY *bd)
11706 {
11707     if(bs && bd){
11708 	if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){
11709 	    PART *ps = bs->nested.part,
11710 		 *pd = bd->nested.part;
11711 	    do				/* for each part */
11712 	      peBodyMoveContents(&ps->body, &pd->body);
11713 	    while ((ps = ps->next) && (pd = pd->next));	/* until done */
11714 	}
11715 	else if(bs->contents.text.data){
11716 	    bd->contents.text.data = bs->contents.text.data;
11717 	    bs->contents.text.data = NULL;
11718 	}
11719     }
11720 }
11721 
11722 
11723 
11724 COMPATT_S *
peGetAttachID(char * h)11725 peGetAttachID(char *h)
11726 {
11727     COMPATT_S *p;
11728 
11729     for(p = peCompAttach; p; p = p->next)
11730       if(!strcmp(p->id, h))
11731 	return(p);
11732 
11733     return(NULL);
11734 }
11735 
11736 
11737 int
peClearAttachID(char * h)11738 peClearAttachID(char *h)
11739 {
11740     COMPATT_S *pp, *pt = NULL;
11741 
11742     for(pp = peCompAttach; pp; pp = pp->next){
11743 	if(!strcmp(pp->id, h)){
11744 	    if(pt)
11745 	      pt->next = pp->next;
11746 	    else
11747 	      peCompAttach = pp->next;
11748 
11749 	    pp->next = NULL;
11750 	    peFreeAttach(&pp);
11751 	    return(TRUE);
11752 	}
11753 
11754 	pt = pp;
11755     }
11756 
11757     return(FALSE);
11758 }
11759 
11760 
11761 /*
11762  * peDoPost - handle preparing header and body text for posting, prepare
11763  *	      for any Fcc, then call the mailer to send it out
11764  */
11765 int
peDoPost(METAENV * metaenv,BODY * body,char * fcc,CONTEXT_S ** fcc_cntxtp,char * errp)11766 peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
11767 {
11768     int   rv = TCL_OK, recipients;
11769     char *s;
11770 
11771     if(commence_fcc(fcc, fcc_cntxtp, TRUE)){
11772 
11773 	wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
11774 	pePrepareForAuthException();
11775 
11776 	if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc))
11777 	   && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){
11778 	    if((s = peAuthException()) != NULL){
11779 		strcpy(errp, s);
11780 	    }
11781 	    else if(wps_global->last_error[0]){
11782 		sprintf(errp, "Send Error: %.*s", 64, wps_global->last_error);
11783 	    }
11784 	    else if(wps_global->c_client_error[0]){
11785 		sprintf(errp, "Send Error: %.*s", 64, wps_global->c_client_error);
11786 	    }
11787 	    else
11788 	      strcpy(errp, "Sending Failure");
11789 
11790 	    rv = TCL_ERROR;
11791 	    dprint((1, "call_mailer failed!"));
11792 	}
11793 	else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){
11794 	    strcpy(errp, "Fcc Failed!.  No message saved.");
11795 	    rv = TCL_ERROR;
11796 	    dprint((1, "explicit fcc write failed!"));
11797 	}
11798 	else{
11799 	    PINEFIELD *pf;
11800 	    REPLY_S *reply = NULL;
11801 
11802 	    /* success, now look for x-reply-uid to flip answered flag for? */
11803 
11804 	    for(pf = metaenv->local; pf && pf->name; pf = pf->next)
11805 	      if(!strucmp(pf->name, "x-reply-uid")){
11806 		  if(pf->textbuf){
11807 		      if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){
11808 
11809 			  update_answered_flags(reply);
11810 
11811 			  if(reply->mailbox)
11812 			    fs_give((void **) &reply->mailbox);
11813 
11814 			  if(reply->prefix)
11815 			    fs_give((void **) &reply->prefix);
11816 
11817 			  if(reply->data.uid.msgs)
11818 			    fs_give((void **) &reply->data.uid.msgs);
11819 
11820 			  fs_give((void **) &reply);
11821 		      }
11822 		  }
11823 
11824 		  break;
11825 	      }
11826 	}
11827     }
11828     else{
11829 	dprint((1,"can't open fcc, cont"));
11830 
11831 	strcpy(errp, "Can't open Fcc");
11832 	rv = TCL_ERROR;
11833     }
11834 
11835     return(rv);
11836 }
11837 
11838 
11839 
11840 /*
11841  * pePostponeCmd - export various bits of alpine state
11842  */
11843 int
PEPostponeCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])11844 PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11845 {
11846     char *err = "PEPostpone: unknown request";
11847     long uidl;
11848     imapuid_t  uid;
11849 
11850     dprint((2, "PEPostponeCmd"));
11851 
11852     if(objc == 1){
11853 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11854     }
11855     else if(!wps_global){
11856 	Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC);
11857 	return(TCL_ERROR);
11858     }
11859     else{
11860 	char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11861 
11862 	if(s1){
11863 	    if(!strcmp(s1, "extract")){
11864 		if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){
11865 		    Tcl_Obj    *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL;
11866 		    MAILSTREAM *stream;
11867 		    BODY       *b;
11868 		    ENVELOPE   *env = NULL;
11869 		    PINEFIELD  *custom = NULL, *cp;
11870 		    REPLY_S    *reply = NULL;
11871 		    ACTION_S   *role = NULL;
11872 		    STORE_S    *so;
11873 		    long	n;
11874 		    int		rv = TCL_OK;
11875 		    char       *fcc = NULL, *lcc = NULL;
11876 		    unsigned	flags = REDRAFT_DEL | REDRAFT_PPND;
11877 
11878 		    uid = uidl;
11879 		    if(objc > 3){			/* optional flags */
11880 			int	  i, nFlags;
11881 			Tcl_Obj **objFlags;
11882 			char	 *flagstr;
11883 
11884 			Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags);
11885 			for(i = 0; i < nFlags; i++){
11886 			    if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
11887 				rv = TCL_ERROR;
11888 			    }
11889 
11890 			    if(!strucmp(flagstr, "html"))
11891 			      flags |= REDRAFT_HTML;
11892 			}
11893 		    }
11894 		    /* BUG: should probably complain if argc > 4 */
11895 
11896 		    if(rv == TCL_OK
11897 		       && postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)
11898 		       && stream){
11899 			if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
11900 			    if((n = mail_msgno(stream, uid)) > 0L){
11901 				if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply,
11902 						NULL, &custom, &role, /* should role be NULL? */
11903 						flags, so)){
11904 				    char *charset = NULL;
11905 
11906 				    /* prepare to package up for caller */
11907 				    objHdr = Tcl_NewListObj(0, NULL);
11908 
11909 				    /* determine body part's charset */
11910 				    if((charset = parameter_val(b->parameter,"charset")) != NULL){
11911 					objOpts = Tcl_NewListObj(0, NULL);
11912 					peAppListF(interp, objOpts, "%s%s", "charset", charset);
11913 					fs_give((void **) &charset);
11914 				    }
11915 
11916 				    /* body part's MIME subtype */
11917 				    if(b->subtype && strucmp(b->subtype,"plain")){
11918 					if(!objOpts)
11919 					  objOpts = Tcl_NewListObj(0, NULL);
11920 
11921 					peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype);
11922 				    }
11923 
11924 				    peAppListF(interp, objHdr, "%s%a", "from",
11925 					       role && role->from ? role->from : env->from);
11926 				    peAppListF(interp, objHdr, "%s%a", "to", env->to);
11927 				    peAppListF(interp, objHdr, "%s%a", "cc", env->cc);
11928 				    peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc);
11929 				    peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to);
11930 				    peAppListF(interp, objHdr, "%s%s", "subject",
11931 					       rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
11932 								      SIZEOF_20KBUF, env->subject));
11933 
11934 				    if(fcc)
11935 				      peFccAppend(interp, objHdr, fcc, -1);
11936 
11937 				    for(cp = custom; cp && cp->name; cp = cp->next)
11938 				      switch(cp->type){
11939 					case Address :
11940 					  strncpy(wtmp_20k_buf, cp->name, SIZEOF_20KBUF);
11941 					  wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11942 					  peAppListF(interp, objHdr, "%s%a",
11943 						     lcase((unsigned char *) wtmp_20k_buf), *cp->addr);
11944 					  break;
11945 
11946 					case Attachment :
11947 					  break;
11948 
11949 					case Fcc :
11950 					case Subject :
11951 					  break; /* ignored */
11952 
11953 					default :
11954 					  strncpy(wtmp_20k_buf, cp->name, SIZEOF_20KBUF);
11955 					  wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11956 					  peAppListF(interp, objHdr, "%s%s",
11957 						     lcase((unsigned char *) wtmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val);
11958 					  break;
11959 				      }
11960 
11961 				    if(reply){
11962 					/* blat x-Reply-UID: for possible use? */
11963 					if(reply->uid){
11964 					    char uidbuf[MAILTMPLEN], *p;
11965 					    long i;
11966 
11967 					    for(i = 0L, p = wtmp_20k_buf; reply->data.uid.msgs[i]; i++){
11968 						if(i)
11969 						  sstrncpy(&p, ",", SIZEOF_20KBUF-(p-wtmp_20k_buf));
11970 
11971 						sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-wtmp_20k_buf));
11972 					    }
11973 
11974 					    wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11975 
11976 					    snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
11977 						     reply->prefix ? int2string(strlen(reply->prefix))
11978 						     : (reply->forwarded) ? "" : "0 ",
11979 						     reply->prefix ? " " : "",
11980 						     reply->prefix ? reply->prefix : "",
11981 						     i, reply->data.uid.validity,
11982 						     wtmp_20k_buf, reply->mailbox);
11983 
11984 					    peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf);
11985 					}
11986 
11987 					fs_give((void **) &reply->mailbox);
11988 					fs_give((void **) &reply->prefix);
11989 					fs_give((void **) &reply->data.uid.msgs);
11990 					fs_give((void **) &reply);
11991 				    }
11992 
11993 				    objBod = Tcl_NewListObj(0, NULL);
11994 				    peSoStrToList(interp, objBod, so);
11995 
11996 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr);
11997 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod);
11998 
11999 				    objAttach = peMsgAttachCollector(interp, b);
12000 
12001 				    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
12002 
12003 				    if(objOpts){
12004 					Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts);
12005 				    }
12006 
12007 				    /* clean up */
12008 				    if(fcc)
12009 				      fs_give((void **) &fcc);
12010 
12011 				    if(lcc)
12012 				      fs_give((void **) &lcc);
12013 
12014 				    mail_free_envelope(&env);
12015 				    pine_free_body(&b);
12016 				    free_action(&role);
12017 
12018 				    /* if Drafts got whacked, open INBOX */
12019 				    if(!wps_global->mail_stream)
12020 				      do_broach_folder(wps_global->inbox_name,
12021 						       wps_global->context_list,
12022 						       NULL, DB_INBOXWOCNTXT);
12023 
12024 				    return(TCL_OK);
12025 				}
12026 
12027 				so_give(&so);
12028 			    }
12029 			    else
12030 			      err = "Unknown UID";
12031 			}
12032 			else
12033 			  err = "No internal storage";
12034 
12035 			/* redraft_work cleaned up the "stream" */
12036 		    }
12037 		    else
12038 		      err = "No Postponed stream";
12039 		}
12040 		else
12041 		  err = "Malformed extract request";
12042 	    }
12043 	    else if(objc == 2){
12044 		if(!strcmp(s1, "any")){
12045 		    MAILSTREAM *stream;
12046 
12047 		    if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12048 			Tcl_SetResult(interp, "1", TCL_STATIC);
12049 
12050 			if(stream != wps_global->mail_stream)
12051 			  pine_mail_close(stream);
12052 		    }
12053 		    else
12054 		      Tcl_SetResult(interp, "0", TCL_STATIC);
12055 
12056 		    return(TCL_OK);
12057 		}
12058 		else if(!strcmp(s1, "count")){
12059 		    MAILSTREAM *stream;
12060 
12061 		    if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12062 			Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC);
12063 
12064 			if(stream != wps_global->mail_stream)
12065 			  pine_mail_close(stream);
12066 		    }
12067 		    else
12068 		      Tcl_SetResult(interp, "-1", TCL_STATIC);
12069 
12070 		    return(TCL_OK);
12071 		}
12072 		else if(!strcmp(s1, "list")){
12073 		    MAILSTREAM *stream;
12074 		    ENVELOPE   *env;
12075 		    Tcl_Obj    *objEnv = NULL, *objEnvList;
12076 		    long	n;
12077 
12078 		    if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12079 			if(!stream->nmsgs){
12080 			    (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND);
12081 			    Tcl_SetResult(interp, "", TCL_STATIC);
12082 			    return(TCL_OK);
12083 			}
12084 
12085 			objEnvList = Tcl_NewListObj(0, NULL);
12086 
12087 			for(n = 1; n <= stream->nmsgs; n++){
12088 			    if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){
12089 				objEnv = Tcl_NewListObj(0, NULL);
12090 
12091 				peAppListF(interp, objEnv, "%s%s", "uid",
12092 					   ulong2string(mail_uid(stream, n)));
12093 
12094 				peAppListF(interp, objEnv, "%s%a", "to", env->to);
12095 
12096 				date_str((char *)env->date, iSDate, 1, wtmp_20k_buf, SIZEOF_20KBUF, 0);
12097 
12098 				peAppListF(interp, objEnv, "%s%s", "date", wtmp_20k_buf);
12099 
12100 				peAppListF(interp, objEnv, "%s%s", "subj",
12101 					   rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
12102 								  SIZEOF_20KBUF, env->subject));
12103 
12104 				Tcl_ListObjAppendElement(interp, objEnvList, objEnv);
12105 			    }
12106 			}
12107 
12108 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList);
12109 
12110 			Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
12111 						 Tcl_NewStringObj("utf-8", -1));
12112 
12113 			if(stream != wps_global->mail_stream)
12114 			  pine_mail_close(stream);
12115 		    }
12116 
12117 		    return(TCL_OK);
12118 		}
12119 	    }
12120 	    else if(objc == 3){
12121 		if(!strcmp(s1, "append")){
12122 		    int rv;
12123 
12124 		    if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_NONE)) == TCL_OK)
12125 		      Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
12126 
12127 		    return(rv);
12128 		}
12129 		else if(!strcmp(s1, "draft")){
12130 		    int rv;
12131 
12132 		    if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_PRSRV_ATT)) == TCL_OK)
12133 		      Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
12134 
12135 		    return(rv);
12136 		}
12137 		else if(!strcmp(s1, "delete")){
12138 		    if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){
12139 			MAILSTREAM    *stream;
12140 			long	       rawno;
12141 
12142 			uid = uidl;
12143 			if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12144 			    if((rawno = mail_msgno(stream, uid)) > 0L){
12145 				mail_flag(stream, long2string(rawno), "\\DELETED", ST_SET);
12146 				wps_global->expunge_in_progress = 1;
12147 				mail_expunge(stream);
12148 				wps_global->expunge_in_progress = 0;
12149 				if(stream != wps_global->mail_stream)
12150 				  pine_mail_actually_close(stream);
12151 
12152 				return(TCL_OK);
12153 			    }
12154 			    else
12155 			      err = "PEPostpone delete: UID no longer exists";
12156 			}
12157 			else
12158 			  err = "PEPostpone delete: No Postponed stream";
12159 		    }
12160 		    else
12161 		      err = "PEPostpone delete: No uid provided";
12162 		}
12163 	    }
12164 	}
12165     }
12166 
12167     Tcl_SetResult(interp, err, TCL_STATIC);
12168     return(TCL_ERROR);
12169 }
12170 
12171 
12172 /*
12173  * peDoPostpone - handle postponing after message collection
12174  */
12175 int
peDoPostpone(METAENV * metaenv,BODY * body,char * fcc,CONTEXT_S ** fcc_cntxtp,char * errp)12176 peDoPostpone(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
12177 {
12178     PINEFIELD   *pf;
12179     int		 rv;
12180     appenduid_t *au;
12181 
12182     /*
12183      * resolve fcc and store it in fcc custom header field data
12184      */
12185     if(fcc && *fcc && fcc_cntxtp && *fcc_cntxtp)
12186       for(pf = metaenv->local; pf && pf->name; pf = pf->next)
12187 	if(!strucmp("fcc", pf->name)){
12188 	    char *name, *rs, path_in_context[MAILTMPLEN];
12189 
12190 	    if(pf->textbuf)			/* free old value */
12191 	      fs_give((void **) &pf->textbuf);
12192 
12193 	    /* replace nickname with full name */
12194 	    if(!(name = folder_is_nick(fcc, FOLDERS(*fcc_cntxtp), FN_NONE)))
12195 	      name = fcc;
12196 
12197 	    if(context_isambig(name) && !(((*fcc_cntxtp)->use) & CNTXT_SAVEDFLT)){
12198 		context_apply(path_in_context, *fcc_cntxtp, name, sizeof(path_in_context));
12199 		rs = IS_REMOTE(path_in_context) ? path_in_context : NULL;
12200 	    }
12201 	    else
12202 	      rs = cpystr(name);
12203 
12204 	    if(rs){
12205 		pf->textbuf = cpystr(rs);
12206 		pf->text = &pf->textbuf;
12207 	    }
12208 
12209 	    break;
12210 	}
12211 
12212     au = mail_parameters(NIL, GET_APPENDUID, NIL);
12213     mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb);
12214 
12215     rv = write_postponed(metaenv, body);
12216 
12217     mail_parameters(NIL, SET_APPENDUID, (void *) au);
12218 
12219     return((rv < 0) ? TCL_ERROR : TCL_OK);
12220 }
12221 
12222 
12223 /*
12224  * peMsgCollector - Collect message parts and call specified handler
12225  */
12226 int
peMsgCollector(Tcl_Interp * interp,int objc,Tcl_Obj ** objv,int (* postfunc)(METAENV *,BODY *,char *,CONTEXT_S **,char *),long flags)12227 peMsgCollector(Tcl_Interp *interp,
12228 	       int objc,
12229 	       Tcl_Obj **objv,
12230 	       int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *),
12231 	       long flags)
12232 {
12233     Tcl_Obj    **objMsg, **objField, **objBody;
12234     int		 i, j, vl, nMsg, nField, nBody;
12235     char	*field, *value, *err = NULL;
12236     MSG_COL_S	 md;
12237     PINEFIELD	*pf;
12238     STRLIST_S	*tp, *lp;
12239     static char	*fakedomain = "@";
12240 
12241     memset(&md, 0, sizeof(MSG_COL_S));
12242     md.postop_fcc_no_attach = -1;
12243     md.postfunc = postfunc;
12244     md.qualified_addrs = ((flags & PMC_FORCE_QUAL) == PMC_FORCE_QUAL);
12245 
12246     if(objc != 1){
12247 	Tcl_SetResult(interp, "Malformed message data", TCL_STATIC);
12248 	return(TCL_ERROR);
12249     }
12250     else if(!wps_global){
12251 	Tcl_SetResult(interp, "No open folder", TCL_STATIC);
12252 	return(TCL_ERROR);
12253     }
12254 
12255     md.outgoing = mail_newenvelope();
12256 
12257     md.metaenv = pine_new_env(md.outgoing, NULL, NULL, md.custom = peCustomHdrs());
12258 
12259     Tcl_ListObjGetElements(interp, objv[0], &nMsg, &objMsg);
12260     for(i = 0; i < nMsg; i++){
12261 	if(Tcl_ListObjGetElements(interp, objMsg[i], &nField, &objField) != TCL_OK){
12262 	    err = "";		/* interp's result object has error message */
12263 	    return(peMsgCollected(interp, &md, err, flags));
12264 	}
12265 
12266 	if(nField && (field = Tcl_GetStringFromObj(objField[0], NULL))){
12267 	    if(!strcmp(field, "body")){
12268 		if(md.msgtext){
12269 		    err = "Too many bodies";
12270 		    return(peMsgCollected(interp, &md, err, flags));
12271 		}
12272 		else if((md.msgtext = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
12273 		    /* mark storage object as user edited */
12274 		    (void) so_attr(md.msgtext, "edited", "1");
12275 
12276 		    Tcl_ListObjGetElements(interp, objField[1], &nBody, &objBody);
12277 		    for(j = 0; j < nBody; j++){
12278 			value = Tcl_GetStringFromObj(objBody[j], &vl);
12279 			if(value){
12280 			    so_nputs(md.msgtext, value, vl);
12281 			    so_puts(md.msgtext, "\n");
12282 			}
12283 			else{
12284 			    err = "Value read failure";
12285 			    return(peMsgCollected(interp, &md, err, flags));
12286 			}
12287 		    }
12288 		}
12289 		else {
12290 		    err = "Can't acquire body storage";
12291 		    return(peMsgCollected(interp, &md, err, flags));
12292 		}
12293 	    }
12294 	    else if(!strucmp(field, "attach")){
12295 		char      *id;
12296 		COMPATT_S *a;
12297 
12298 		if(nField == 2
12299 		   && (id = Tcl_GetStringFromObj(objField[1], NULL))
12300 		   && (a = peGetAttachID(id))){
12301 		    tp = new_strlist(id);
12302 		    if((lp = md.attach) != NULL){
12303 			do
12304 			  if(!lp->next){
12305 			      lp->next = tp;
12306 			      break;
12307 			  }
12308 			while((lp = lp->next) != NULL);
12309 		    }
12310 		    else
12311 		      md.attach = tp;
12312 		}
12313 		else{
12314 		    strcpy(err = wtmp_20k_buf, "Unknown attachment ID");
12315 		    return(peMsgCollected(interp, &md, err, flags));
12316 		}
12317 	    }
12318 	    else if(!strucmp(field, "fcc")){
12319 		Tcl_Obj **objFcc;
12320 		int	  nFcc;
12321 
12322 		if(Tcl_ListObjGetElements(interp, objField[1], &nFcc, &objFcc) == TCL_OK
12323 		   && nFcc == 2
12324 		   && Tcl_GetIntFromObj(interp, objFcc[0], &md.fcc_colid) == TCL_OK
12325 		   && (value = Tcl_GetStringFromObj(objFcc[1], NULL))){
12326 		    if(md.fcc)
12327 		      fs_give((void **) &md.fcc);
12328 
12329 		    md.fcc = cpystr(value);
12330 		}
12331 		else {
12332 		    strcpy(err = wtmp_20k_buf, "Unrecognized Fcc specification");
12333 		    return(peMsgCollected(interp, &md, err, flags));
12334 		}
12335 	    }
12336 	    else if(!strucmp(field, "postoption")){
12337 		Tcl_Obj **objPO;
12338 		int	  nPO, ival;
12339 
12340 		value = NULL;
12341 		if(Tcl_ListObjGetElements(interp, objField[1], &nPO, &objPO) == TCL_OK
12342 		   && nPO == 2
12343 		   && (value = Tcl_GetStringFromObj(objPO[0], NULL))){
12344 		    if(!strucmp(value,"fcc-without-attachments")){
12345 			if(Tcl_GetIntFromObj(interp, objPO[1], &ival) == TCL_OK){
12346 			    md.postop_fcc_no_attach = (ival != 0);
12347 			}
12348 			else{
12349 			    sprintf(err = wtmp_20k_buf, "Malformed Post Option: fcc-without-attachments");
12350 			    return(peMsgCollected(interp, &md, err, flags));
12351 			}
12352 		    }
12353 		    else if(!strucmp(value, "charset")){
12354 			if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12355 			    char *p;
12356 
12357 			    for(p = value; ; p++){		/* sanity check */
12358 				if(!*p){
12359 				    md.charset = cpystr(value);
12360 				    break;
12361 				}
12362 
12363 				if(isspace((unsigned char ) *p)
12364 				   || !isprint((unsigned char) *p))
12365 				  break;
12366 
12367 				if(p - value > 255)
12368 				  break;
12369 			    }
12370 			}
12371 			else{
12372 			    err = "Post option read failure";
12373 			    return(peMsgCollected(interp, &md, err, flags));
12374 			}
12375 		    }
12376 		    else if(!strucmp(value, "flowed")){
12377 			if(F_OFF(F_QUELL_FLOWED_TEXT,wps_global)){
12378 			    if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12379 				if(!strucmp(value, "yes"))
12380 				  md.flowed = 1;
12381 			    }
12382 			    else{
12383 				err = "Post option read failure";
12384 				return(peMsgCollected(interp, &md, err, flags));
12385 			    }
12386 			}
12387 		    }
12388 		    else if(!strucmp(value, "subtype")){
12389 			if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12390 			    if(!strucmp(value, "html"))
12391 			      md.html = 1;
12392 			}
12393 			else{
12394 			    err = "Post option read failure";
12395 			    return(peMsgCollected(interp, &md, err, flags));
12396 			}
12397 		    }
12398 		    else if(!strucmp(value, "priority")){
12399 			if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12400 			    char *priority = NULL;
12401 
12402 			    if(!strucmp(value, "highest"))
12403 			      priority = "Highest";
12404 			    else if(!strucmp(value, "high"))
12405 			      priority = "High";
12406 			    else if(!strucmp(value, "normal"))
12407 			      priority = "Normal";
12408 			    else if(!strucmp(value, "low"))
12409 			      priority = "Low";
12410 			    else if(!strucmp(value, "lowest"))
12411 			      priority = "Lowest";
12412 
12413 			    if(priority){
12414 				if((pf = set_priority_header(md.metaenv, priority)) != NULL)
12415 				  pf->text = &pf->textbuf;
12416 			    }
12417 			}
12418 			else{
12419 			    err = "Post option read failure";
12420 			    return(peMsgCollected(interp, &md, err, flags));
12421 			}
12422 		    }
12423 		    else{
12424 			sprintf(err = wtmp_20k_buf, "Unknown Post Option: %s", value);
12425 			return(peMsgCollected(interp, &md, err, flags));
12426 		    }
12427 		}
12428 		else{
12429 		    sprintf(err = wtmp_20k_buf, "Malformed Post Option");
12430 		    return(peMsgCollected(interp, &md, err, flags));
12431 		}
12432 	    }
12433 	    else {
12434 		if(nField != 2){
12435 		    sprintf(err = wtmp_20k_buf, "Malformed header (%s)", field);
12436 		    return(peMsgCollected(interp, &md, err, flags));
12437 		}
12438 
12439 		if((value = Tcl_GetStringFromObj(objField[1], &vl)) != NULL){
12440 		    ADDRESS **addrp = NULL;
12441 		    char    **valp = NULL, *valcpy;
12442 
12443 		    if(!strucmp(field, "from")){
12444 			addrp = &md.outgoing->from;
12445 		    }
12446 		    else if(!strucmp(field, "reply-to")){
12447 			addrp = &md.outgoing->reply_to;
12448 		    }
12449 		    else if(!strucmp(field, "to")){
12450 			addrp = &md.outgoing->to;
12451 		    }
12452 		    else if(!strucmp(field, "cc")){
12453 			addrp = &md.outgoing->cc;
12454 		    }
12455 		    else if(!strucmp(field, "bcc")){
12456 			addrp = &md.outgoing->bcc;
12457 		    }
12458 		    else if(!strucmp(field, "subject")){
12459 			valp = &md.outgoing->subject;
12460 		    }
12461 		    else if(!strucmp(field, "in-reply-to")){
12462 			valp = &md.outgoing->in_reply_to;
12463 		    }
12464 		    else if(!strucmp(field, "newsgroups")){
12465 			valp = &md.outgoing->newsgroups;
12466 		    }
12467 		    else if(!strucmp(field, "followup-to")){
12468 			valp = &md.outgoing->followup_to;
12469 		    }
12470 		    else if(!strucmp(field, "references")){
12471 			valp = &md.outgoing->references;
12472 		    }
12473 		    else if(!strucmp(field, "x-reply-uid")){
12474 			for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
12475 			  if(!strucmp(pf->name, "x-reply-uid")){
12476 			      valp = pf->text = &pf->textbuf;
12477 			      break;
12478 			  }
12479 		    }
12480 		    else if(!strucmp(field, "x-auth-received")){
12481 			for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
12482 			  if(!strucmp(pf->name, "x-auth-received")){
12483 			      valp = pf->text = &pf->textbuf;
12484 			      break;
12485 			  }
12486 		    }
12487 		    else{
12488 			for(pf = md.metaenv->custom; pf && pf->name; pf = pf->next)
12489 			  if(!strucmp(field, pf->name)){
12490 			      if(pf->type == Address)
12491 				addrp = pf->addr;
12492 			      else if(vl)
12493 				valp = &pf->textbuf;
12494 			      else if(pf->textbuf)
12495 				fs_give((void **) &pf->textbuf);
12496 
12497 			      break;
12498 			  }
12499 
12500 			if(!pf)
12501 			  dprint((2, "\nPOST: unrecognized field - %s\n", field));
12502 		    }
12503 
12504 		    if(valp){
12505 			if(*valp)
12506 			  fs_give((void **) valp);
12507 
12508 			sprintf(*valp = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
12509 		    }
12510 
12511 		    if(addrp){
12512 			sprintf(valcpy = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
12513 
12514 			for(; *addrp; addrp = &(*addrp)->next)
12515 			  ;
12516 
12517 			rfc822_parse_adrlist(addrp, valcpy,
12518 					     (flags & PMC_FORCE_QUAL)
12519 					       ? fakedomain : wps_global->maildomain);
12520 			fs_give((void **) &valcpy);
12521 		    }
12522 		}
12523 		else{
12524 		    err = "Value read failure";
12525 		    return(peMsgCollected(interp, &md, err, flags));
12526 		}
12527 	    }
12528 	}
12529     }
12530 
12531     return(peMsgCollected(interp, &md, err, flags));
12532 }
12533 
12534 
12535 /*
12536  * peMsgCollected - Dispatch collected message data and cleanup
12537  */
12538 int
peMsgCollected(Tcl_Interp * interp,MSG_COL_S * md,char * err,long flags)12539 peMsgCollected(Tcl_Interp *interp, MSG_COL_S *md, char *err, long flags)
12540 {
12541     int		   rv = TCL_OK, non_ascii = FALSE;
12542     unsigned char  c;
12543     BODY	  *body = NULL, *tbp = NULL;
12544     char	   errbuf[WP_MAX_POST_ERROR + 1], *charset;
12545     STRLIST_S	  *lp;
12546 
12547     if(err){
12548 	if(md->msgtext)
12549 	  so_give(&md->msgtext);
12550 
12551 	rv = TCL_ERROR;
12552     }
12553     else if(md->qualified_addrs && check_addresses(md->metaenv) == CA_BAD){
12554 	sprintf(err = wtmp_20k_buf, "Address must be fully qualified.");
12555 	rv = TCL_ERROR;
12556     }
12557     else{
12558 	/* sniff body for possible multipart wrapping to protect encoding */
12559 	so_seek(md->msgtext, 0L, 0);
12560 
12561 	while(so_readc(&c, md->msgtext))
12562 	  if(!c || c & 0x80){
12563 	      non_ascii = TRUE;
12564 	      break;
12565 	  }
12566 
12567 	if(!md->outgoing->from)
12568 	  md->outgoing->from = generate_from();
12569 
12570 	rfc822_date(wtmp_20k_buf);
12571 	md->outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
12572 	md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from);
12573 	md->outgoing->message_id = generate_message_id(NULL);
12574 
12575 	body = mail_newbody();
12576 
12577 	/* wire any attachments to body */
12578 	if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, wps_global))){
12579 	    PART	  **np;
12580 	    PARAMETER **pp;
12581 	    COMPATT_S  *a;
12582 
12583 	    /* setup slot for message text */
12584 	    body->type	  = TYPEMULTIPART;
12585 	    body->nested.part = mail_newbody_part();
12586 	    tbp		  = &body->nested.part->body;
12587 
12588 	    /* link in attachments */
12589 	    for(lp = md->attach, np = &body->nested.part->next; lp; lp = lp->next, np = &(*np)->next){
12590 		if(!(a = peGetAttachID(lp->name))){
12591 		    err = "Unknown Attachment ID";
12592 		    rv = TCL_ERROR;
12593 		    break;
12594 		}
12595 
12596 		*np = mail_newbody_part();
12597 
12598 		if(a->file){
12599 		    (*np)->body.id = generate_message_id(NULL);
12600 		    (*np)->body.description = cpystr(a->l.f.description);
12601 
12602 		    /* set name parameter */
12603 		    for(pp = &(*np)->body.parameter; *pp; )
12604 		      if(!struncmp((*pp)->attribute, "name", 4)
12605 			 && (!*((*pp)->attribute + 4)
12606 			     || *((*pp)->attribute + 4) == '*')){
12607 			  PARAMETER *free_me = *pp;
12608 			  *pp = (*pp)->next;
12609 			  free_me->next = NULL;
12610 			  mail_free_body_parameter(&free_me);
12611 		      }
12612 		      else
12613 			pp = &(*pp)->next;
12614 
12615 		    *pp = NULL;
12616 		    set_parameter(pp, "name", a->l.f.remote);
12617 
12618 		    /* Then set the Content-Disposition ala RFC1806 */
12619 		    if(!(*np)->body.disposition.type){
12620 			(*np)->body.disposition.type = cpystr("attachment");
12621 			for(pp = &(*np)->body.disposition.parameter; *pp; )
12622 			  if(!struncmp((*pp)->attribute, "filename", 4)
12623 			     && (!*((*pp)->attribute + 4)
12624 				 || *((*pp)->attribute + 4) == '*')){
12625 			      PARAMETER *free_me = *pp;
12626 			      *pp = (*pp)->next;
12627 			      free_me->next = NULL;
12628 			      mail_free_body_parameter(&free_me);
12629 			  }
12630 			  else
12631 			    pp = &(*pp)->next;
12632 
12633 			*pp = NULL;
12634 			set_parameter(pp, "filename", a->l.f.remote);
12635 		    }
12636 
12637 		    if(((*np)->body.contents.text.data = (void *) so_get(FileStar, a->l.f.local, READ_ACCESS)) != NULL){
12638 			(*np)->body.type     = mt_translate_type(a->l.f.type);
12639 			(*np)->body.subtype  = cpystr(a->l.f.subtype);
12640 			(*np)->body.encoding = ENCBINARY;
12641 			(*np)->body.size.bytes = name_file_size(a->l.f.local);
12642 
12643 			if((*np)->body.type == TYPEOTHER
12644 			   && !set_mime_type_by_extension(&(*np)->body, a->l.f.local))
12645 			  set_mime_type_by_grope(&(*np)->body);
12646 
12647 			so_release((STORE_S *)(*np)->body.contents.text.data);
12648 		    }
12649 		    else{
12650 			/* unravel here */
12651 			err = "Can't open uploaded attachment";
12652 			rv = TCL_ERROR;
12653 			break;
12654 		    }
12655 		}
12656 		else if(a->body){
12657 		    BODY *newbody = copy_body(NULL, a->l.b.body);
12658 		    (*np)->body = *newbody;
12659 		    fs_give((void **) &newbody);
12660 		    peBodyMoveContents(a->l.b.body, &(*np)->body);
12661 		}
12662 		else{
12663 		    err = "BOTCH: Unknown attachment type";
12664 		    rv = TCL_ERROR;
12665 		    break;
12666 		}
12667 	    }
12668 	}
12669 	else
12670 	  tbp = body;
12671 
12672 	/* assign MIME parameters to text body part */
12673 	tbp->type = TYPETEXT;
12674 	if(md->html) tbp->subtype = cpystr("HTML");
12675 
12676 	tbp->contents.text.data = (void *) md->msgtext;
12677 	tbp->encoding = ENCOTHER;
12678 
12679 	/* set any text flowed param */
12680 	if(md->flowed)
12681 	  peMsgSetParm(&tbp->parameter, "format", "flowed");
12682 
12683 	if(rv == TCL_OK){
12684 	    CONTEXT_S *fcc_cntxt = wps_global->context_list;
12685 
12686 	    while(md->fcc_colid--)
12687 	      if(fcc_cntxt->next)
12688 		fcc_cntxt = fcc_cntxt->next;
12689 
12690 	    if(md->postop_fcc_no_attach >= 0){
12691 		int oldval = F_ON(F_NO_FCC_ATTACH, wps_global);
12692 		F_SET(F_NO_FCC_ATTACH, wps_global, md->postop_fcc_no_attach);
12693 		md->postop_fcc_no_attach = oldval;
12694 	    }
12695 
12696 	    pine_encode_body(body);
12697 
12698 	    rv = (*md->postfunc)(md->metaenv, body, md->fcc, &fcc_cntxt, errbuf);
12699 
12700 	    if(md->postop_fcc_no_attach >= 0){
12701 		F_SET(F_NO_FCC_ATTACH, wps_global, md->postop_fcc_no_attach);
12702 	    }
12703 
12704 	    if(rv == TCL_OK){
12705 		if((flags & PMC_PRSRV_ATT) == 0)
12706 		  peFreeAttach(&peCompAttach);
12707 	    }
12708 	    else{
12709 		/* maintain pointers to attachments */
12710 		(void) peMsgAttachCollector(NULL, body);
12711 		err = errbuf;
12712 	    }
12713 	}
12714 
12715 	pine_free_body(&body);
12716     }
12717 
12718     if(md->charset)
12719       fs_give((void **) &md->charset);
12720 
12721     free_strlist(&md->attach);
12722 
12723     pine_free_env(&md->metaenv);
12724 
12725     if(md->custom)
12726       free_customs(md->custom);
12727 
12728     mail_free_envelope(&md->outgoing);
12729 
12730     if(err && *err)
12731       Tcl_SetResult(interp, err, TCL_VOLATILE);
12732 
12733     return(rv);
12734 }
12735 
12736 
12737 void
peMsgSetParm(PARAMETER ** pp,char * pa,char * pv)12738 peMsgSetParm(PARAMETER **pp, char *pa, char *pv)
12739 {
12740     for(; *pp; pp = &(*pp)->next)
12741       if(!strucmp(pa, (*pp)->attribute)){
12742 	if((*pp)->value)
12743 	  fs_give((void **) &(*pp)->value);
12744 
12745 	break;
12746       }
12747 
12748     if(!*pp){
12749 	*pp = mail_newbody_parameter();
12750 	(*pp)->attribute = cpystr(pa);
12751     }
12752 
12753     (*pp)->value = cpystr(pv);
12754 }
12755 
12756 
12757 Tcl_Obj *
peMsgAttachCollector(Tcl_Interp * interp,BODY * b)12758 peMsgAttachCollector(Tcl_Interp *interp, BODY *b)
12759 {
12760     char      *id, *name = NULL;
12761     PART      *part;
12762     Tcl_Obj   *aListObj = NULL, *aObj = NULL;
12763 
12764     peFreeAttach(&peCompAttach);
12765 
12766     if(interp)
12767       aListObj = Tcl_NewListObj(0, NULL);
12768 
12769     if(b->type == TYPEMULTIPART){
12770 	/*
12771 	 * Walk first level, clipping branches and adding them
12772 	 * to the attachment list...
12773 	 */
12774 	for(part = b->nested.part->next; part; part = part->next) {
12775 	    id	 = peBodyAttachID(&part->body);
12776 	    aObj = Tcl_NewListObj(0, NULL);
12777 
12778 	    if(interp){
12779 		Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(id, -1));
12780 
12781 		/* name */
12782 		if((name = get_filename_parameter(NULL, 0, &part->body, NULL)) != NULL){
12783 		    Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(name, -1));
12784 		    fs_give((void **) &name);
12785 		}
12786 		else
12787 		  Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj("Unknown", -1));
12788 
12789 		/* size */
12790 		Tcl_ListObjAppendElement(interp, aObj,
12791 					 Tcl_NewLongObj((part->body.encoding == ENCBASE64)
12792 							? ((part->body.size.bytes * 3)/4)
12793 							: part->body.size.bytes));
12794 
12795 		/* type */
12796 		snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
12797 			 body_type_names(part->body.type),
12798 			 part->body.subtype ? part->body.subtype : rfc822_default_subtype (part->body.type));
12799 		Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(wtmp_20k_buf, -1));
12800 		Tcl_ListObjAppendElement(interp, aListObj, aObj);
12801 	    }
12802 	}
12803     }
12804 
12805     return (aListObj);
12806 }
12807 
12808 
12809 int
peFccAppend(Tcl_Interp * interp,Tcl_Obj * obj,char * fcc,int colid)12810 peFccAppend(Tcl_Interp *interp, Tcl_Obj *obj, char *fcc, int colid)
12811 {
12812     Tcl_Obj *objfcc = NULL;
12813 
12814     if(colid < 0)
12815       colid = (wps_global->context_list && (wps_global->context_list->use & CNTXT_INCMNG)) ? 1 : 0;
12816 
12817     return((objfcc = Tcl_NewListObj(0, NULL))
12818 	   && Tcl_ListObjAppendElement(interp, objfcc, Tcl_NewStringObj("fcc", -1)) == TCL_OK
12819 	   && peAppListF(interp, objfcc, "%i%s", colid, fcc) == TCL_OK
12820 	   && Tcl_ListObjAppendElement(interp, obj, objfcc) == TCL_OK);
12821 }
12822 
12823 
12824 /* * * * * * * * * * * * *  Start of Address Management Routines * * * * * * * * * * * */
12825 
12826 
12827 /*
12828  * PEAddressCmd - export various bits of address book/directory access
12829  */
12830 int
PEAddressCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])12831 PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
12832 {
12833     char *op;
12834 
12835     dprint((2, "PEAddressCmd"));
12836 
12837     if(objc == 1){
12838 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
12839 	return(TCL_ERROR);
12840     }
12841     else if(!wps_global){
12842 	Tcl_SetResult(interp, "PEAddress: no open folder", TCL_STATIC);
12843 	return(TCL_ERROR);
12844     }
12845     else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
12846 	if(objc == 2){
12847 	    if(!strcmp(op, "safecheck")){
12848 		if(peInitAddrbooks(interp, 1) != TCL_OK)
12849 		  return(TCL_ERROR);
12850 		return(TCL_OK);
12851 	    }
12852 	    else if(!strcmp(op, "books")){
12853 		int   i;
12854 
12855 		/*
12856 		 * return the list of configured address books
12857 		 */
12858 
12859 		if(peInitAddrbooks(interp, 0) != TCL_OK)
12860 		  return(TCL_ERROR);
12861 
12862 		for(i = 0; i < as.n_addrbk; i++){
12863 		    Tcl_Obj *objmv[4];
12864 
12865 		    objmv[0] = Tcl_NewIntObj(i);
12866 		    if(as.adrbks[i].abnick){
12867 			objmv[1] = Tcl_NewStringObj(as.adrbks[i].abnick, -1);
12868 		    }
12869 		    else {
12870 			char buf[256];
12871 
12872 			snprintf(buf, sizeof(buf), "Address book number %d", i + 1);
12873 			objmv[1] = Tcl_NewStringObj(buf, -1);
12874 		    }
12875 
12876 		    objmv[2] = Tcl_NewStringObj(as.adrbks[i].filename ? as.adrbks[i].filename : "", -1);
12877 
12878 		    objmv[3] = Tcl_NewIntObj(as.adrbks[i].access == ReadWrite);
12879 
12880 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
12881 					     Tcl_NewListObj(4, objmv));
12882 		}
12883 
12884 		return(TCL_OK);
12885 	    }
12886 	}
12887 	else if(objc == 3){
12888 	    if(!strcmp(op, "parselist")){
12889 		char	    *addrstr;
12890 		ADDRESS     *addrlist = NULL, *atmp, *anextp;
12891 		static char *fakedomain = "@";
12892 
12893 		if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
12894 		  addrstr = cpystr(addrstr); /* can't munge tcl copy */
12895 
12896 		wps_global->c_client_error[0] = '\0';
12897 		rfc822_parse_adrlist(&addrlist, addrstr,
12898 				     (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
12899 				       ? fakedomain : wps_global->maildomain);
12900 
12901 		fs_give((void **) &addrstr);
12902 		if(wps_global->c_client_error[0]){
12903 		    Tcl_SetResult(interp, wps_global->c_client_error, TCL_STATIC);
12904 		    return(TCL_ERROR);
12905 		}
12906 
12907 		for(atmp = addrlist; atmp; ){
12908 		    RFC822BUFFER rbuf;
12909 
12910 		    anextp = atmp->next;
12911 		    atmp->next = NULL;
12912 		    wtmp_20k_buf[0] = '\0';
12913 		    rbuf.f   = dummy_soutr;
12914 		    rbuf.s   = NULL;
12915 		    rbuf.beg = wtmp_20k_buf;
12916 		    rbuf.cur = wtmp_20k_buf;
12917 		    rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
12918 		    rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
12919 		    *rbuf.cur = '\0';
12920 		    Tcl_ListObjAppendElement(interp,
12921 					     Tcl_GetObjResult(interp),
12922 					     Tcl_NewStringObj(wtmp_20k_buf, -1));
12923 		    atmp = anextp;
12924 		}
12925 
12926 		mail_free_address(&addrlist);
12927 		return(TCL_OK);
12928 	    }
12929 	    else if(!strcmp(op, "xlookup")){
12930 		char	    *addrstr;
12931 		ADDRESS     *addrlist = NULL, *atmp, *anextp;
12932 		static char *fakedomain = "@";
12933 
12934 		if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
12935 		  addrstr = cpystr(addrstr); /* can't munge tcl copy */
12936 
12937 		wps_global->c_client_error[0] = '\0';
12938 		rfc822_parse_adrlist(&addrlist, addrstr,
12939 				     (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
12940 				       ? fakedomain : wps_global->maildomain);
12941 
12942 		fs_give((void **) &addrstr);
12943 		if(wps_global->c_client_error[0]){
12944 		    Tcl_SetResult(interp, wps_global->c_client_error, TCL_STATIC);
12945 		    return(TCL_ERROR);
12946 		}
12947 
12948 		for(atmp = addrlist; atmp; ){
12949 		    anextp = atmp->next;
12950 		    atmp->next = NULL;
12951 		    wtmp_20k_buf[0] = '\0';
12952 		    if(atmp->host){
12953 			if(atmp->host[0] == '@'){
12954 			    /* leading ampersand means "missing-hostname" */
12955 			}
12956 			else{
12957 			    RFC822BUFFER rbuf;
12958 
12959 			    rbuf.f   = dummy_soutr;
12960 			    rbuf.s   = NULL;
12961 			    rbuf.beg = wtmp_20k_buf;
12962 			    rbuf.cur = wtmp_20k_buf;
12963 			    rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
12964 			    rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
12965 			    *rbuf.cur = '\0';
12966 			    Tcl_ListObjAppendElement(interp,
12967 						     Tcl_GetObjResult(interp),
12968 						     Tcl_NewStringObj(wtmp_20k_buf, -1));
12969 			}
12970 		    } /* else group syntax, move on */
12971 
12972 		    atmp = anextp;
12973 		}
12974 
12975 		mail_free_address(&addrlist);
12976 		return(TCL_OK);
12977 	    }
12978 	    else if(!strcmp(op, "format")){
12979 		int	     i, booknum;
12980 		char	     buf[256], *s;
12981 
12982 		if(peInitAddrbooks(interp, 0) != TCL_OK)
12983 		  return(TCL_ERROR);
12984 
12985 		/*
12986 		 *
12987 		 */
12988 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
12989 		  for(i = 0; i < as.n_addrbk; i++)
12990 		    if(i == booknum){
12991 			addrbook_new_disp_form(&as.adrbks[booknum], wps_global->VAR_ABOOK_FORMATS, booknum, NULL);
12992 
12993 			for(i = 0; i < NFIELDS && as.adrbks[booknum].disp_form[i].type != Notused; i++){
12994 			    switch(as.adrbks[booknum].disp_form[i].type){
12995 			      case Nickname :
12996 				s = "nick";
12997 				break;
12998 			      case Fullname :
12999 				s = "full";
13000 				break;
13001 			      case Addr :
13002 				s = "addr";
13003 				break;
13004 			      case Filecopy :
13005 				s = "fcc";
13006 				break;
13007 			      case Comment :
13008 				s = "comment";
13009 				break;
13010 			      default :
13011 				s = NULL;
13012 				break;
13013 			    }
13014 
13015 			    if(s){
13016 				Tcl_Obj *objmv[2];
13017 
13018 				objmv[0] = Tcl_NewStringObj(s, -1);
13019 				objmv[1] = Tcl_NewIntObj((100 * as.adrbks[booknum].disp_form[i].width) / wps_global->ttyo->screen_cols);
13020 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13021 							 Tcl_NewListObj(2, objmv));
13022 			    }
13023 			}
13024 
13025 
13026 			return(TCL_OK);
13027 		    }
13028 
13029 		snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
13030 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
13031 		return(TCL_ERROR);
13032 	    }
13033 	    else if(!strcmp(op, "list")){
13034 		int	     i, j, k, n, booknum;
13035 		char	     buf[256], *s;
13036 		AdrBk_Entry *ae;
13037 		Tcl_Obj	    *objev[NFIELDS + 1], *objhv[2];
13038 
13039 		if(peInitAddrbooks(interp, 0) != TCL_OK)
13040 		  return(TCL_ERROR);
13041 
13042 		/*
13043 		 *
13044 		 */
13045 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
13046 		  for(i = 0; i < as.n_addrbk; i++)
13047 		    if(i == booknum){
13048 			addrbook_new_disp_form(&as.adrbks[booknum], wps_global->VAR_ABOOK_FORMATS, booknum, NULL);
13049 
13050 			for(i = 0;
13051 			    (ae = adrbk_get_ae(as.adrbks[booknum].address_book, i));
13052 			    i++){
13053 
13054 			    /* first member is type: Single, List or Lookup */
13055 			    switch(ae->tag){
13056 			      case Single :
13057 				s = "single";
13058 				break;
13059 			      case List :
13060 				s = "list";
13061 				break;
13062 			      default :	/* not set!?! */
13063 				continue;
13064 			    }
13065 
13066 			    if(!ae->nickname)
13067 			      continue;
13068 
13069 			    objhv[0] = Tcl_NewStringObj(ae->nickname, -1);
13070 			    objhv[1] = Tcl_NewStringObj(s, -1);
13071 			    objev[n = 0] = Tcl_NewListObj(2, objhv);
13072 
13073 			    /*
13074 			     * set fields based on VAR_ABOOK_FORMATS
13075 			     */
13076 
13077 			    for(j = 0; j < NFIELDS && as.adrbks[booknum].disp_form[j].type != Notused; j++){
13078 				switch(as.adrbks[booknum].disp_form[j].type){
13079 				  case Nickname :
13080 				    objev[++n] = Tcl_NewStringObj(ae->nickname, -1);
13081 				    break;
13082 				  case Fullname :
13083 				    objev[++n] = Tcl_NewStringObj(ae->fullname, -1);
13084 				    break;
13085 				  case Addr :
13086 				    if(ae->tag == Single){
13087 					objev[++n] = Tcl_NewStringObj(ae->addr.addr, -1);
13088 				    }
13089 				    else{
13090 					Tcl_Obj **objav;
13091 
13092 					for(k = 0; ae->addr.list[k]; k++)
13093 					  ;
13094 
13095 					objav = (Tcl_Obj **) fs_get(k * sizeof(Tcl_Obj *));
13096 					for(k = 0; ae->addr.list[k]; k++)
13097 					  objav[k] = Tcl_NewStringObj(ae->addr.list[k], -1);
13098 
13099 					objev[++n] = Tcl_NewListObj(k, objav);
13100 					fs_give((void **) &objav);
13101 				    }
13102 				    break;
13103 				  case Filecopy :
13104 				    objev[++n] = Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1);
13105 				    break;
13106 				  case Comment :
13107 				    objev[++n] = Tcl_NewStringObj(ae->extra ? ae->extra : "", -1);
13108 				    break;
13109 				  default :
13110 				    s = NULL;
13111 				    break;
13112 				}
13113 			    }
13114 
13115 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13116 						     Tcl_NewListObj(n + 1, objev));
13117 			}
13118 
13119 			return(TCL_OK);
13120 		    }
13121 
13122 		snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
13123 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
13124 		return(TCL_ERROR);
13125 	    }
13126 	}
13127 	else if(objc == 4){
13128 	    if(!strcmp(op, "verify")){
13129 	      /*
13130 	       * The object here is to check the following list of field values
13131 	       * to see that they are valid address list, expanding if necessary.
13132 	       * The first argument is the list of field values, with "to" being
13133 	       * first.  The second arg is the current fcc value.
13134 	       *
13135 	       * The return value is of the following form:
13136 	       *
13137 	       * { {{errstr {{oldstr newstr {ldap-opts ...}} ...}} ...} newfcc}
13138 	       */
13139 	        Tcl_Obj **objVal;
13140 		char	    *addrstr, *newaddr = NULL, *error = NULL,
13141 		            *tstr1, *tstr2, *fcc, *newfcc = NULL;
13142 		BuildTo	     toaddr;
13143 		int	     rv, badadrs, i , numlistvals,
13144 		             numldapqueries = 0;
13145 		Tcl_Obj     *resObj = NULL, *secObj, *strObj, *adrObj, *res2Obj;
13146 #ifdef ENABLE_LDAP
13147 		WPLDAPRES_S **tsl;
13148 
13149 		wpldap_global->query_no++;
13150 		if(wpldap_global->ldap_search_list){
13151 		    wpldap_global->ldap_search_list =
13152 		      free_wpldapres(wpldap_global->ldap_search_list);
13153 		}
13154 
13155 		tsl = &(wpldap_global->ldap_search_list);
13156 #endif /* ENABLE_LDAP */
13157 
13158 		if(Tcl_ListObjGetElements(interp, objv[2], &numlistvals,
13159 					  &objVal) == TCL_OK){
13160 		    if((fcc = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
13161 		      return TCL_ERROR;
13162 		    res2Obj = Tcl_NewListObj(0, NULL);
13163 		    for(i = 0; i < numlistvals; i++){
13164 			size_t l;
13165 
13166 		        if((addrstr = Tcl_GetStringFromObj(objVal[i], NULL)) == NULL)
13167 			  return TCL_ERROR;
13168 
13169 			addrstr = cpystr(addrstr); /* can't munge tcl copy */
13170 			toaddr.type    = Str;
13171 			toaddr.arg.str = cpystr(addrstr);
13172 			l = strlen(addrstr);
13173 			badadrs = 0;
13174 			resObj = Tcl_NewListObj(0, NULL);
13175 			secObj = Tcl_NewListObj(0, NULL);
13176 			for(tstr1 = addrstr; tstr1; tstr1 = tstr2){
13177 			    tstr2 = strqchr(tstr1, ',', 0, -1);
13178 			    if(tstr2)
13179 			      *tstr2 = '\0';
13180 
13181 			    strncpy(toaddr.arg.str, tstr1, l);
13182 			    toaddr.arg.str[l] = '\0';
13183 
13184 			    removing_leading_and_trailing_white_space(toaddr.arg.str);
13185 			    if(*toaddr.arg.str){
13186 				if(i == 0 && tstr1 == addrstr)
13187 				  newfcc = cpystr(fcc);
13188 
13189 			        rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
13190 
13191 				if(rv == 0){
13192 				    strObj = Tcl_NewListObj(0, NULL);
13193 				    Tcl_ListObjAppendElement(interp, strObj,
13194 							     Tcl_NewStringObj(toaddr.arg.str, -1));
13195 				    Tcl_ListObjAppendElement(interp, strObj,
13196 							     Tcl_NewStringObj(newaddr,-1));
13197 				    /* append whether or not ldap stuff
13198 				     * was returned
13199 				     */
13200 				    adrObj = Tcl_NewListObj(0,NULL);
13201 #ifdef ENABLE_LDAP
13202 				    if(*tsl) {
13203 				        LDAP_CHOOSE_S   *tres;
13204 				        LDAP_SERV_RES_S *trl;
13205 					LDAPMessage *e;
13206 					ADDRESS *newadr;
13207 					char    *ret_to;
13208 
13209 					tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
13210 				        for(trl = (*tsl)->reslist; trl;
13211 					    trl = trl->next){
13212 					    for(e = ldap_first_entry(trl->ld,
13213 								     trl->res);
13214 						e != NULL;
13215 						e = ldap_next_entry(trl->ld, e)){
13216 					        tres->ld        = trl->ld;
13217 						tres->selected_entry = e;
13218 						tres->info_used = trl->info_used;
13219 						tres->serv      = trl->serv;
13220 						if((newadr = address_from_ldap(tres)) != NULL){
13221 						    if(newadr->mailbox && newadr->host){
13222 							RFC822BUFFER rbuf;
13223 							size_t len;
13224 
13225 							len = est_size(newadr);
13226 						        ret_to = (char *)fs_get(len * sizeof(char));
13227 							ret_to[0] = '\0';
13228 							rbuf.f   = dummy_soutr;
13229 							rbuf.s   = NULL;
13230 							rbuf.beg = ret_to;
13231 							rbuf.cur = ret_to;
13232 							rbuf.end = ret_to+len-1;
13233 							rfc822_output_address_list(&rbuf, newadr, 0L, NULL);
13234 							*rbuf.cur = '\0';
13235 							Tcl_ListObjAppendElement(interp,
13236 								    adrObj, Tcl_NewStringObj(ret_to, -1));
13237 							fs_give((void **)&ret_to);
13238 						    }
13239 						    mail_free_address(&newadr);
13240 						}
13241 					    }
13242 					}
13243 					fs_give((void **)&tres);
13244 					numldapqueries++;
13245 					tsl = &((*tsl)->next);
13246 				    }
13247 #endif /* ENABLE_LDAP */
13248 				    Tcl_ListObjAppendElement(interp, strObj, adrObj);
13249 				    Tcl_ListObjAppendElement(interp, secObj, strObj);
13250 				}
13251 				else {
13252 				  badadrs = 1;
13253 				  break;
13254 				}
13255 			    }
13256 			    if(tstr2){
13257 			      *tstr2 = ',';
13258 			      tstr2++;
13259 			    }
13260 			}
13261 			resObj = Tcl_NewListObj(0, NULL);
13262 			Tcl_ListObjAppendElement(interp, resObj,
13263 						 Tcl_NewStringObj(badadrs
13264 						 ? (error ? error : "Unknown")
13265 						 : "", -1));
13266 			Tcl_ListObjAppendElement(interp, resObj, secObj);
13267 			Tcl_ListObjAppendElement(interp, res2Obj, resObj);
13268 			fs_give((void **) &addrstr);
13269 			fs_give((void **) &toaddr.arg.str);
13270 		    }
13271 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), res2Obj);
13272 		    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13273 					     Tcl_NewStringObj(newfcc ? newfcc
13274 						     : (fcc ? fcc : ""), -1));
13275 		    if(newfcc) fs_give((void **)&newfcc);
13276 		    return(TCL_OK);
13277 		}
13278 		return(TCL_ERROR);
13279 	    }
13280 	    else if(!strcmp(op, "expand")){
13281 		BuildTo	     toaddr;
13282 		char	    *addrstr, *newaddr = NULL, *error = NULL, *fcc, *newfcc = NULL;
13283 		int	     rv;
13284 
13285 		/*
13286 		 * Return value will be of the form:
13287 		 *  {"addrstr",
13288 		 *   ldap-query-number,
13289 		 *   "fcc"
13290 		 *  }
13291 		 *
13292 		 *  ldap-query-number will be nonzero if
13293 		 *  there is something interesting to display as a result
13294 		 *  of an ldap query.
13295 		 */
13296 
13297 		/*
13298 		 * Given what looks like an rfc822 address line, parse the
13299 		 * contents and expand any tokens that look like they ought
13300 		 * to be.
13301 		 */
13302 
13303 		if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
13304 		    toaddr.type = Str;
13305 		    toaddr.arg.str = cpystr(addrstr); /* can't munge tcl copy */
13306 		    fcc = Tcl_GetStringFromObj(objv[3], NULL);
13307 #ifdef ENABLE_LDAP
13308 		    wpldap_global->query_no++;
13309 		    if(wpldap_global->ldap_search_list){
13310 		      wpldap_global->ldap_search_list =
13311 			free_wpldapres(wpldap_global->ldap_search_list);
13312 		    }
13313 #endif /* ENABLE_LDAP */
13314 		    newfcc = cpystr(fcc);
13315 		    rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
13316 		    fs_give((void **) &toaddr.arg.str);
13317 		    if(rv == 0){
13318 #ifdef ENABLE_LDAP
13319 		      /*
13320 		       * c-client quotes results with spaces in them, so we'll go
13321 		       * through and unquote them.
13322 		       */
13323 			if(wpldap_global->ldap_search_list){
13324 			    WPLDAPRES_S *tres;
13325 			    char        *tstr1, *tstr2;
13326 			    char        *qstr1, *newnewaddr;
13327 			    int          qstr1len;
13328 
13329 			    for(tres = wpldap_global->ldap_search_list;
13330 				tres; tres = tres->next){
13331 			        if(strqchr(tres->str, ' ', 0, -1)){
13332 				    qstr1len = strlen(tres->str) + 3;
13333 				    qstr1 = (char *)fs_get(qstr1len*sizeof(char));
13334 				    snprintf(qstr1, qstr1len, "\"%.*s\"", qstr1len, tres->str);
13335 				    for(tstr1 = newaddr; tstr1; tstr1 = tstr2){
13336 				        tstr2 = strqchr(tstr1, ',', 0, -1);
13337 					if(strncmp(qstr1, tstr1, tstr2 ? tstr2 - tstr1
13338 						   : strlen(tstr1)) == 0){
13339 					    size_t l;
13340 					    l = strlen(newaddr) + strlen(tres->str) + 2
13341 						 + (tstr2 ? strlen(tstr2) : 0);
13342 					    newnewaddr = (char *) fs_get(l * sizeof(char));
13343 					    snprintf(newnewaddr, l, "%.*s%s%s", (int) (tstr1 - newaddr),
13344 						    newaddr, tres->str, tstr2 ? tstr2 : "");
13345 					    fs_give((void **)&newaddr);
13346 					    newaddr = newnewaddr;
13347 					    break;
13348 					}
13349 					if(tstr2)
13350 					  tstr2++;
13351 					if(tstr2 && *tstr2 == ' ')
13352 					  tstr2++;
13353 				    }
13354 				    if(qstr1)
13355 				      fs_give((void **) &qstr1);
13356 			        }
13357 			    }
13358 			}
13359 #endif /* ENABLE_LDAP */
13360 		      	if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13361 				    Tcl_NewStringObj(newaddr, -1)) != TCL_OK)
13362 			  return(TCL_ERROR);
13363 #ifdef ENABLE_LDAP
13364 			if(wpldap_global->ldap_search_list){
13365 			  if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13366 				      Tcl_NewIntObj(wpldap_global->query_no)) != TCL_OK)
13367 			      return(TCL_ERROR);
13368 			}
13369 			else
13370 #endif /* ENABLE_LDAP */
13371 		      	if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13372 				   Tcl_NewIntObj(0)) != TCL_OK)
13373 			  return(TCL_ERROR);
13374 			if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13375 						    Tcl_NewStringObj(newfcc ? newfcc
13376 						    : (fcc ? fcc : ""), -1)) != TCL_OK)
13377 			  return(TCL_ERROR);
13378 			if(newfcc) fs_give((void **)&newfcc);
13379 
13380 			return(TCL_OK);
13381 		    }
13382 		    else{
13383 			Tcl_SetResult(interp, error ? error : "Indeterminate error", TCL_VOLATILE);
13384 			if(newfcc) fs_give((void **)&newfcc);
13385 			return(TCL_ERROR);
13386 		    }
13387 		}
13388 	    }
13389 	    else if(!strcmp(op, "complete")){
13390 		/*
13391 		 * CMD: complete uid
13392 		 *
13393 		 *    Look for possible completions for
13394 		 *    given query_string.
13395 		 *
13396 		 * ARGS: <query_string> <uid>
13397 		 *
13398 		 * Returns: candidate list: {nickname  {personal mailbox}}
13399 		 */
13400 		char	    *query, *errstr;
13401 		long	     uid;
13402 		COMPLETE_S  *completions, *cp;
13403 
13404 		if(peInitAddrbooks(interp, 0) == TCL_OK){
13405 		    if((query = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
13406 			if(Tcl_GetLongFromObj(interp, objv[3], &uid) == TCL_OK){
13407 
13408 			    completions = adrbk_list_of_completions(query,
13409 								    wps_global->mail_stream,
13410 								    uid,
13411 #ifdef ENABLE_LDAP
13412 								    ((strlen(query) >= 5) ? ALC_INCLUDE_LDAP : 0) |
13413 #endif /* ENABLE_LDAP */
13414 								    0);
13415 
13416 			    if(completions){
13417 				for(cp = completions; cp; cp = cp->next)
13418 				  peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s",
13419 					     cp->nickname ? cp->nickname : "",
13420 					     cp->full_address ? cp->full_address : "",
13421 					     cp->fcc ? cp->fcc : "");
13422 
13423 				free_complete_s(&completions);
13424 			    }
13425 			    else
13426 			      Tcl_SetResult(interp, "", TCL_STATIC);
13427 
13428 			    return(TCL_OK);
13429 			}
13430 			else
13431 			  errstr = "PEAddress: Cannot read UID";
13432 		    }
13433 		    else
13434 		      errstr = "PEAddress: Cannot get completion query";
13435 		}
13436 		else
13437 		  errstr = "PEAddress: Address Book initialization failed";
13438 
13439 		Tcl_SetResult(interp, errstr, TCL_STATIC);
13440 		return(TCL_ERROR);
13441 	    }
13442 	}
13443 	else if(objc == 5){
13444 	    if(!strcmp(op, "entry")){
13445 		int	     booknum, i, aindex;
13446 		char	    *nick, *astr = NULL, *errstr = NULL, *fccstr = NULL, buf[128];
13447 		AdrBk_Entry *ae;
13448 		BuildTo      bldto;
13449 
13450 		if(peInitAddrbooks(interp, 0) != TCL_OK)
13451 		  return(TCL_ERROR);
13452 
13453 		/*
13454 		 * Given an address book handle and nickname, return address
13455 		 */
13456 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
13457 		  for(i = 0; i < as.n_addrbk; i++)
13458 		    if(i == booknum){
13459 			if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL){
13460 			    Tcl_SetResult(interp, "PEAddress list: Can't get nickname", TCL_STATIC);
13461 			    return(TCL_ERROR);
13462 			}
13463 			if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
13464 			    || (*nick == '\0' && aindex < 0)){
13465 			    Tcl_SetResult(interp, "PEAddress list: Can't get aindex", TCL_STATIC);
13466 			    return(TCL_ERROR);
13467 			}
13468 			if((*nick)
13469 			   ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
13470 			   : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
13471 			    bldto.type    = Abe;
13472 			    bldto.arg.abe = ae;
13473 
13474 			    (void) our_build_address(bldto, &astr, &errstr, &fccstr, NULL);
13475 
13476 			    if(errstr){
13477 				if(astr)
13478 				  fs_give((void **) &astr);
13479 
13480 				Tcl_SetResult(interp, errstr, TCL_VOLATILE);
13481 				return(TCL_ERROR);
13482 			    }
13483 
13484 			    if(astr){
13485 				char    *p;
13486 				int	 l;
13487 
13488 				l = (4*strlen(astr) + 1) * sizeof(char);
13489 				p = (char *) fs_get(l);
13490 				if(rfc1522_decode_to_utf8((unsigned char *) p, l, astr) == (unsigned char *) p){
13491 				    fs_give((void **) &astr);
13492 				    astr = p;
13493 				}
13494 				else
13495 				  fs_give((void **)&p);
13496 			    }
13497 			}
13498 
13499 			if(astr){
13500 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13501 						     Tcl_NewStringObj(astr, -1));
13502 			    fs_give((void **) &astr);
13503 
13504 			    if(fccstr){
13505 				Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13506 							 Tcl_NewStringObj(*fccstr ? fccstr : "\"\"", -1));
13507 				fs_give((void **) &fccstr);
13508 			    }
13509 			    else
13510 			      Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13511 						       Tcl_NewStringObj("", -1));
13512 			}
13513 			else
13514 			  Tcl_SetResult(interp, "", TCL_STATIC);
13515 
13516 			return(TCL_OK);
13517 		    }
13518 
13519 		snprintf(buf, sizeof(buf), "PEAddress list: unknown address book ID %d", booknum);
13520 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
13521 		return(TCL_ERROR);
13522 	    }
13523 	    else if(!strcmp(op, "fullentry")){
13524 		int	     booknum, j, aindex;
13525 		char	    *nick;
13526 		AdrBk_Entry *ae;
13527 		Tcl_Obj     *resObj;
13528 
13529 		if(peInitAddrbooks(interp, 0) != TCL_OK)
13530 		  return(TCL_ERROR);
13531 
13532 		/*
13533 		 * Given an address book handle and nickname, return
13534 		 * nickname, fullname, address(es), fcc, and comments
13535 		 */
13536 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13537 		    if(booknum >= 0 && booknum < as.n_addrbk){
13538 		        if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
13539 			  return(TCL_ERROR);
13540 			if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
13541 			    || (*nick == '\0' && aindex < 0))
13542 			  return(TCL_ERROR);
13543 			if((*nick)
13544 			   ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
13545 			   : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
13546 			    Tcl_ListObjAppendElement(interp,
13547 						     Tcl_GetObjResult(interp),
13548 						     Tcl_NewStringObj(ae->nickname ? ae->nickname : "", -1));
13549 			    Tcl_ListObjAppendElement(interp,
13550 						     Tcl_GetObjResult(interp),
13551 						     Tcl_NewStringObj(ae->fullname ? ae->fullname : "", -1));
13552 			    resObj = Tcl_NewListObj(0,NULL);
13553 			    if(ae->tag == Single)
13554 			      Tcl_ListObjAppendElement(interp,
13555 						       resObj,
13556 						       Tcl_NewStringObj(ae->addr.addr ? ae->addr.addr : "", -1));
13557 			    else {
13558 				for(j = 0; ae->addr.list[j]; j++)
13559 				  Tcl_ListObjAppendElement(interp, resObj,
13560 							   Tcl_NewStringObj(ae->addr.list[j] ? ae->addr.list[j] : "", -1));
13561 			    }
13562 			    Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj);
13563 			    Tcl_ListObjAppendElement(interp,
13564 						     Tcl_GetObjResult(interp),
13565 						     Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1));
13566 			    Tcl_ListObjAppendElement(interp,
13567 						     Tcl_GetObjResult(interp),
13568 						     Tcl_NewStringObj(ae->extra ? ae->extra : "", -1));
13569 			    return(TCL_OK);
13570 			}
13571 		    }
13572 		}
13573 		return(TCL_ERROR);
13574 	    }
13575 	    else if(!strcmp(op, "delete")){
13576 	        char *nick, buf[256];
13577 		int booknum, aindex;
13578 		adrbk_cntr_t old_entry;
13579 		AdrBk *ab;
13580 		if(peInitAddrbooks(interp, 0) != TCL_OK){
13581 		    snprintf(buf, sizeof(buf), "PEAddress delete: couldn't init addressbooks");
13582 		    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13583 		    return(TCL_ERROR);
13584 		}
13585 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13586 		    nick    = Tcl_GetStringFromObj(objv[3], NULL);
13587 		    removing_leading_and_trailing_white_space(nick);
13588 		}
13589 		else
13590 		  return(TCL_ERROR);
13591 		if(booknum >= 0 && booknum < as.n_addrbk) {
13592 		    if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
13593 		    ab = as.adrbks[booknum].address_book;
13594 		}
13595 		else{
13596 		    snprintf(buf, sizeof(buf), "PEAddress delete: Book number out of range");
13597 		    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13598 		    return(TCL_ERROR);
13599 		}
13600 		if((Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK)
13601 		   || (*nick == '\0' && aindex < 0))
13602 		  return(TCL_ERROR);
13603 		adrbk_check_validity(ab, 1L);
13604 		if(ab->flags & FILE_OUTOFDATE ||
13605 		   (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
13606 		    Tcl_SetResult(interp,
13607 				  "Address book out of sync.  Cannot update at this moment",
13608 				  TCL_VOLATILE);
13609 		    return(TCL_ERROR);
13610 		}
13611 		if(!nick){
13612 		    snprintf(buf, sizeof(buf), "PEAddress delete: No nickname");
13613 		    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13614 		    return(TCL_ERROR);
13615 		}
13616 		if((*nick)
13617 		   ? (!adrbk_lookup_by_nick(ab, nick, &old_entry))
13618 		   : ((old_entry = (adrbk_cntr_t)aindex) == -1)){
13619 		    snprintf(buf, sizeof(buf), "PEAddress delete: Nickname \"%.128s\" not found", nick);
13620 		    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13621 		    return(TCL_ERROR);
13622 		}
13623 		if(adrbk_delete(ab, old_entry, 0, 0, 1, 1)){
13624 		    snprintf(buf, sizeof(buf), "PEAddress delete: Couldn't delete addressbook entry");
13625 		    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13626 		    return(TCL_ERROR);
13627 		}
13628 		return(TCL_OK);
13629 	    }
13630 	}
13631 	else if((objc == 10 || objc == 11) && !strcmp(op, "edit")){
13632 	    if(!strcmp(op, "edit")){
13633 	        int           booknum, adri, add, rv, aindex;
13634 		char         *nick, *fn, *fcc, *comment, *addrfield,
13635 		              buf[256], **addrs, *orignick = NULL;
13636 		AdrBk_Entry  *ae = NULL;
13637 		AdrBk        *ab;
13638 		adrbk_cntr_t  old_entry = NO_NEXT, new_entry;
13639 		Tag           tag;
13640 		ADDRESS      *adr = NULL;
13641 
13642 
13643 		if(peInitAddrbooks(interp, 0) != TCL_OK)
13644 		    return(TCL_ERROR);
13645 		if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13646 		    if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
13647 		    nick    = Tcl_GetStringFromObj(objv[3], NULL);
13648 		    removing_leading_and_trailing_white_space(nick);
13649 		    if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK){
13650 			Tcl_SetResult(interp, "No Address Handle", TCL_VOLATILE);
13651 			return(TCL_ERROR);
13652 		    }
13653 		    fn      = Tcl_GetStringFromObj(objv[5], NULL);
13654 		    removing_leading_and_trailing_white_space(fn);
13655 		    if(!*fn) fn = NULL;
13656 		    addrfield = Tcl_GetStringFromObj(objv[6], NULL);
13657 		    removing_leading_and_trailing_white_space(addrfield);
13658 		    if(!*addrfield) addrfield = NULL;
13659 		    /*
13660 		    if(Tcl_ListObjGetElements(interp, objv[7], &numlistvals, &objVal) != TCL_OK)
13661 		      return(TCL_ERROR);
13662 		    */
13663 		    fcc     = Tcl_GetStringFromObj(objv[7], NULL);
13664 		    removing_leading_and_trailing_white_space(fcc);
13665 		    if(!*fcc) fcc = NULL;
13666 		    comment = Tcl_GetStringFromObj(objv[8], NULL);
13667 		    removing_leading_and_trailing_white_space(comment);
13668 		    if(!*comment) comment = NULL;
13669 		    if(Tcl_GetIntFromObj(interp, objv[9], &add) != TCL_OK)
13670 		      return(TCL_ERROR);
13671 		    if(objc == 11) {
13672 		        /*
13673 			 * if objc == 11 then that means that they changed the
13674 			 * value of nick to something else, and this one is the
13675 			 * original nick
13676 			 */
13677 		        orignick     = Tcl_GetStringFromObj(objv[10], NULL);
13678 			removing_leading_and_trailing_white_space(orignick);
13679 		    }
13680 		    if((addrs = parse_addrlist(addrfield)) != NULL){
13681 		      int tbuflen = strlen(addrfield);
13682 		      char *tbuf;
13683 		      if(!(tbuf = (char *) fs_get(sizeof(char) * (tbuflen+128)))){
13684 			Tcl_SetResult(interp, "malloc error", TCL_VOLATILE);
13685 			fs_give((void **) &addrs);
13686 			return(TCL_ERROR);
13687 		      }
13688 		      for(adri = 0; addrs[adri]; adri++){
13689 			  if(*(addrs[adri])){
13690 			      wps_global->c_client_error[0] = '\0';
13691 			      strncpy(tbuf, addrs[adri], tbuflen+128);
13692 			      tbuf[tbuflen+128-1] = '\0';
13693 			      rfc822_parse_adrlist(&adr, tbuf, "@");
13694 			      if(adr) mail_free_address(&adr);
13695 			      adr = NULL;
13696 			      if(wps_global->c_client_error[0]){
13697 				snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s",
13698 					addrs[adri], strlen(addrs[adri]) > 10 ?
13699 					"..." : "", wps_global->c_client_error);
13700 				Tcl_SetResult(interp, buf, TCL_VOLATILE);
13701 				if(tbuf)
13702 				  fs_give((void **) &tbuf);
13703 				fs_give((void **) &addrs);
13704 				return(TCL_ERROR);
13705 			      }
13706 			  }
13707 		      }
13708 		      if(tbuf) fs_give((void **)&tbuf);
13709 		    }
13710 		    else adri = 0;
13711 
13712 		    /* addrs[adri] = NULL; */
13713 
13714 		    if(adri > 1) tag = List;
13715 		    else tag = Single;
13716 
13717 		    if(booknum >= 0 && booknum < as.n_addrbk) {
13718 		        ab = as.adrbks[booknum].address_book;
13719 		    }
13720 		    else{
13721 		      if(addrs)
13722 		        fs_give((void **) &addrs);
13723 		      return(TCL_ERROR);
13724 		    }
13725 		    adrbk_check_validity(ab, 1L);
13726 		    if(ab->flags & FILE_OUTOFDATE ||
13727 		       (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
13728 			Tcl_SetResult(interp,
13729 				      "Address book out of sync.  Cannot update at this moment",
13730 				      TCL_VOLATILE);
13731 			return(TCL_ERROR);
13732 		    }
13733 		    if(aindex >= 0){
13734 		        ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex);
13735 			if(ae){
13736 			    old_entry = (adrbk_cntr_t) aindex;
13737 			}
13738 			else{
13739 			    Tcl_SetResult(interp, "No Address Handle!", TCL_VOLATILE);
13740 			    return(TCL_ERROR);
13741 			}
13742 		    }
13743 		    else if(nick && *nick && adrbk_lookup_by_nick(ab, nick, NULL)){
13744 		        snprintf(buf, sizeof(buf), "Entry with nickname %.128s already exists.",
13745 				nick);
13746 			Tcl_SetResult(interp, buf, TCL_VOLATILE);
13747 		        if(addrs)
13748 		          fs_give((void **) &addrs);
13749 			return(TCL_ERROR);
13750 		    }
13751 		    if(ae &&
13752 		       ((tag == List && ae->tag == Single) ||
13753 			(tag == Single && ae->tag == List))){
13754 		        if(adrbk_delete(ab, old_entry, 0,0,1,0)){
13755 			    snprintf(buf, sizeof(buf), "Problem updating from %s to %s.",
13756 				    ae->tag == Single ? "Single" : "List",
13757 				    tag == List ? "List" : "Single");
13758 			    Tcl_SetResult(interp, buf, TCL_VOLATILE);
13759 			    if(addrs)
13760 			      fs_give((void **) &addrs);
13761 			    return(TCL_ERROR);
13762 			}
13763 			old_entry = NO_NEXT;
13764 		    }
13765 		    if((rv = adrbk_add(ab, old_entry,
13766 				       nick ? nick : "",
13767 				       fn ? fn : "",
13768 				       tag == List ? (char *)addrs :
13769 				       (addrs && *addrs) ? *addrs : "",
13770 				       fcc ? fcc : "",
13771 				       comment ? comment : "",
13772 				       tag, &new_entry, NULL, 0, 1,
13773 				       tag == List ? 0 : 1)) != 0){
13774 		        snprintf(buf, sizeof(buf), "Couldn't add entry! rv=%d.", rv);
13775 			Tcl_SetResult(interp, buf, TCL_VOLATILE);
13776 			if(addrs)
13777 			  fs_give((void **) &addrs);
13778 			return(TCL_ERROR);
13779 		    }
13780 		    if(tag == List) {
13781 		      adrbk_listdel_all(ab, new_entry);
13782 		      adrbk_nlistadd(ab, new_entry, NULL, NULL, addrs, 0, 1, 1);
13783 		    }
13784 		    return(TCL_OK);
13785 		}
13786 		snprintf(buf, sizeof(buf), "Unknown address book ID %d", booknum);
13787 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
13788 		return(TCL_ERROR);
13789 	    }
13790 	}
13791     }
13792 
13793     Tcl_SetResult(interp, "PEAddress: unrecognized command", TCL_STATIC);
13794     return(TCL_ERROR);
13795 }
13796 
13797 
13798 int
peInitAddrbooks(Tcl_Interp * interp,int safe)13799 peInitAddrbooks(Tcl_Interp *interp, int safe)
13800 {
13801     if(wps_global->remote_abook_validity > 0)
13802       (void)adrbk_check_and_fix_all(safe, 0, 0);
13803 
13804     if(!init_addrbooks(NoDisplay, 1, 1, 0)){
13805 	Tcl_SetResult(interp, "No Address Book Configured", TCL_STATIC);
13806 	return(TCL_ERROR);
13807     }
13808 
13809     return(TCL_OK);
13810 }
13811 
13812 
13813 
13814 int
peRuleStatVal(char * str,int * n)13815 peRuleStatVal(char *str, int  *n)
13816 {
13817     if(!str)
13818       return(1);
13819 
13820     if(!strcmp(str, "either"))
13821       *n = PAT_STAT_EITHER;
13822     else if(!strcmp(str, "yes"))
13823       *n = PAT_STAT_YES;
13824     else if(!strcmp(str, "no"))
13825       *n = PAT_STAT_NO;
13826     else
13827       return 1;
13828 
13829     return 0;
13830 }
13831 
13832 
13833 #define RS_RULE_EDIT      0x0001
13834 #define RS_RULE_ADD       0x0002
13835 #define RS_RULE_DELETE    0x0004
13836 #define RS_RULE_SHUFFUP   0x0008
13837 #define RS_RULE_SHUFFDOWN 0x0010
13838 #define RS_RULE_GETPAT    0x0100
13839 #define RS_RULE_FINDPAT   0x0200
13840 
13841 int
peRuleSet(Tcl_Interp * interp,Tcl_Obj ** objv)13842 peRuleSet(Tcl_Interp *interp, Tcl_Obj **objv)
13843 {
13844     char *rule, *patvar, *patval, *actvar, *actval, *tstr, *ruleaction;
13845     int rno, nPat, nPatEmnt, nAct, nActEmnt, i, rv = 0;
13846     Tcl_Obj **objPat, **objPatEmnt, **objAct, **objActEmnt;
13847     long rflags = PAT_USE_CHANGED, aflags = 0;
13848     PAT_STATE pstate;
13849     PAT_S *pat, *new_pat;
13850 
13851     if(!(rule = Tcl_GetStringFromObj(objv[0], NULL)))
13852       return(TCL_ERROR);
13853 
13854     if(!(ruleaction = Tcl_GetStringFromObj(objv[1], NULL)))
13855       return(TCL_ERROR);
13856 
13857     if(Tcl_GetIntFromObj(interp, objv[2], &rno) == TCL_ERROR)
13858       return(TCL_ERROR);
13859 
13860     if(!(strcmp(rule, "filter")))
13861       rflags |= ROLE_DO_FILTER;
13862     else if(!(strcmp(rule, "score")))
13863       rflags |= ROLE_DO_SCORES;
13864     else if(!(strcmp(rule, "indexcolor")))
13865       rflags |= ROLE_DO_INCOLS;
13866     else
13867       return(TCL_ERROR);
13868 
13869     if(!(strcmp(ruleaction, "edit"))){
13870       aflags |= RS_RULE_EDIT;
13871       aflags |= RS_RULE_GETPAT;
13872       aflags |= RS_RULE_FINDPAT;
13873     }
13874     else if(!(strcmp(ruleaction, "add"))){
13875       aflags |= RS_RULE_ADD;
13876       aflags |= RS_RULE_GETPAT;
13877     }
13878     else if(!(strcmp(ruleaction, "delete"))){
13879       aflags |= RS_RULE_DELETE;
13880       aflags |= RS_RULE_FINDPAT;
13881     }
13882     else if(!(strcmp(ruleaction, "shuffup"))){
13883       aflags |= RS_RULE_SHUFFUP;
13884       aflags |= RS_RULE_FINDPAT;
13885     }
13886     else if(!(strcmp(ruleaction, "shuffdown"))){
13887       aflags |= RS_RULE_SHUFFDOWN;
13888       aflags |= RS_RULE_FINDPAT;
13889     }
13890     else return(TCL_ERROR);
13891 
13892     if(aflags & RS_RULE_FINDPAT){
13893 	if(any_patterns(rflags, &pstate)){
13894 	    for(pat = first_pattern(&pstate), i = 0;
13895 		pat && i != rno;
13896 		pat = next_pattern(&pstate), i++);
13897 	    if(i != rno) return(TCL_ERROR);
13898 	}
13899     }
13900     if(aflags & RS_RULE_GETPAT){
13901 	int tcl_error = 0;
13902 
13903 	Tcl_ListObjGetElements(interp, objv[3], &nPat, &objPat);
13904 	Tcl_ListObjGetElements(interp, objv[4], &nAct, &objAct);
13905 
13906 	new_pat = (PAT_S *)fs_get(sizeof(PAT_S));
13907 	memset(new_pat, 0, sizeof(PAT_S));
13908 	new_pat->patgrp = (PATGRP_S *)fs_get(sizeof(PATGRP_S));
13909 	memset(new_pat->patgrp, 0, sizeof(PATGRP_S));
13910 	new_pat->action = (ACTION_S *)fs_get(sizeof(ACTION_S));
13911 	memset(new_pat->action, 0, sizeof(ACTION_S));
13912 
13913 	/* Set up the pattern group */
13914 	for(i = 0; i < nPat; i++){
13915 	    Tcl_ListObjGetElements(interp, objPat[i], &nPatEmnt, &objPatEmnt);
13916 	    if(nPatEmnt != 2) return(TCL_ERROR);
13917 	    patvar = Tcl_GetStringFromObj(objPatEmnt[0], NULL);
13918 	    patval = Tcl_GetStringFromObj(objPatEmnt[1], NULL);
13919 	    if(!patvar || !patval) return(TCL_ERROR);
13920 
13921 	    tstr = NULL;
13922 	    if(*patval){
13923 		tstr = cpystr(patval);
13924 		removing_leading_and_trailing_white_space(tstr);
13925 		if(!(*tstr))
13926 		  fs_give((void **) &tstr);
13927 	    }
13928 
13929 	    if(!(strcmp(patvar, "nickname"))){
13930 		new_pat->patgrp->nick = tstr;
13931 		tstr = NULL;
13932 	    }
13933 	    else if(!(strcmp(patvar, "comment"))){
13934 		new_pat->patgrp->comment = tstr;
13935 		tstr = NULL;
13936 	    }
13937 	    else if(!(strcmp(patvar, "to"))){
13938 		new_pat->patgrp->to = string_to_pattern(tstr);
13939 	    }
13940 	    else if(!(strcmp(patvar, "from"))){
13941 		new_pat->patgrp->from = string_to_pattern(tstr);
13942 	    }
13943 	    else if(!(strcmp(patvar, "sender"))){
13944 		new_pat->patgrp->sender = string_to_pattern(tstr);
13945 	    }
13946 	    else if(!(strcmp(patvar, "cc"))){
13947 		new_pat->patgrp->cc = string_to_pattern(tstr);
13948 	    }
13949 	    else if(!(strcmp(patvar, "recip"))){
13950 		new_pat->patgrp->recip = string_to_pattern(tstr);
13951 	    }
13952 	    else if(!(strcmp(patvar, "partic"))){
13953 		new_pat->patgrp->partic = string_to_pattern(tstr);
13954 	    }
13955 	    else if(!(strcmp(patvar, "news"))){
13956 		new_pat->patgrp->news = string_to_pattern(tstr);
13957 	    }
13958 	    else if(!(strcmp(patvar, "subj"))){
13959 		new_pat->patgrp->subj = string_to_pattern(tstr);
13960 	    }
13961 	    else if(!(strcmp(patvar, "bodytext"))){
13962 		new_pat->patgrp->bodytext = string_to_pattern(tstr);
13963 	    }
13964 	    else if(!(strcmp(patvar, "alltext"))){
13965 		new_pat->patgrp->alltext = string_to_pattern(tstr);
13966 	    }
13967 	    else if(!(strcmp(patvar, "keyword"))){
13968 		new_pat->patgrp->keyword = string_to_pattern(tstr);
13969 	    }
13970 	    else if(!(strcmp(patvar, "charset"))){
13971 		new_pat->patgrp->charsets = string_to_pattern(tstr);
13972 	    }
13973 	    else if(!(strcmp(patvar, "ftype"))){
13974 		if(!tstr) return(TCL_ERROR);
13975 
13976 		if(!(strcmp(tstr, "any")))
13977 		  new_pat->patgrp->fldr_type = FLDR_ANY;
13978 		else if(!(strcmp(tstr, "news")))
13979 		  new_pat->patgrp->fldr_type = FLDR_NEWS;
13980 		else if(!(strcmp(tstr, "email")))
13981 		  new_pat->patgrp->fldr_type = FLDR_EMAIL;
13982 		else if(!(strcmp(tstr, "specific")))
13983 		  new_pat->patgrp->fldr_type = FLDR_SPECIFIC;
13984 		else{
13985 		    free_pat(&new_pat);
13986 		    return(TCL_ERROR);
13987 		}
13988 	    }
13989 	    else if(!(strcmp(patvar, "folder"))){
13990 		new_pat->patgrp->folder = string_to_pattern(tstr);
13991 	    }
13992 	    else if(!(strcmp(patvar, "stat_new"))){
13993 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_new)){
13994 		    free_pat(&new_pat);
13995 		    tcl_error++;
13996 		}
13997 	    }
13998 	    else if(!(strcmp(patvar, "stat_rec"))){
13999 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_rec)){
14000 		    free_pat(&new_pat);
14001 		    tcl_error++;
14002 		}
14003 	    }
14004 	    else if(!(strcmp(patvar, "stat_del"))){
14005 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_del)){
14006 		    free_pat(&new_pat);
14007 		    tcl_error++;
14008 		}
14009 	    }
14010 	    else if(!(strcmp(patvar, "stat_imp"))){
14011 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_imp)){
14012 		    free_pat(&new_pat);
14013 		    tcl_error++;
14014 		}
14015 	    }
14016 	    else if(!(strcmp(patvar, "stat_ans"))){
14017 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_ans)){
14018 		    free_pat(&new_pat);
14019 		    tcl_error++;
14020 		}
14021 	    }
14022 	    else if(!(strcmp(patvar, "stat_8bitsubj"))){
14023 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_8bitsubj)){
14024 		    free_pat(&new_pat);
14025 		    tcl_error++;
14026 		}
14027 	    }
14028 	    else if(!(strcmp(patvar, "stat_bom"))){
14029 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_bom)){
14030 		    free_pat(&new_pat);
14031 		    tcl_error++;
14032 		}
14033 	    }
14034 	    else if(!(strcmp(patvar, "stat_boy"))){
14035 		if(peRuleStatVal(tstr, &new_pat->patgrp->stat_boy)){
14036 		    free_pat(&new_pat);
14037 		    tcl_error++;
14038 		}
14039 	    }
14040 	    else if(!(strcmp(patvar, "age"))){
14041 		new_pat->patgrp->age = parse_intvl(tstr);
14042 	    }
14043 	    else if(!(strcmp(patvar, "size"))){
14044 		new_pat->patgrp->size = parse_intvl(tstr);
14045 	    }
14046 	    else if(!(strcmp(patvar, "score"))){
14047 		new_pat->patgrp->score = parse_intvl(tstr);
14048 	    }
14049 	    else if(!(strcmp(patvar, "addrbook"))){
14050 		if(tstr){
14051 		    if(!strcmp(tstr, "either"))
14052 		      new_pat->patgrp->inabook = IAB_EITHER;
14053 		    else if(!strcmp(tstr, "yes"))
14054 		      new_pat->patgrp->inabook = IAB_YES;
14055 		    else if(!strcmp(tstr, "no"))
14056 		      new_pat->patgrp->inabook = IAB_NO;
14057 		    else if(!strcmp(tstr, "yesspecific"))
14058 		      new_pat->patgrp->inabook = IAB_SPEC_YES;
14059 		    else if(!strcmp(tstr, "nospecific"))
14060 		      new_pat->patgrp->inabook = IAB_SPEC_NO;
14061 		    else
14062 		      tcl_error++;
14063 		}
14064 		else
14065 		  tcl_error++;
14066 
14067 		if(tcl_error)
14068 		  free_pat(&new_pat);
14069 	    }
14070 	    else if(!(strcmp(patvar, "specificabook"))){
14071 		new_pat->patgrp->abooks = string_to_pattern(tstr);
14072 	    }
14073 	    else if(!(strcmp(patvar, "headers"))){
14074 		ARBHDR_S **ahp;
14075 		int	   nHdrList, nHdrPair, n;
14076 		Tcl_Obj  **objHdrList, **objHdrPair;
14077 
14078 		Tcl_ListObjGetElements(interp, objPatEmnt[1], &nHdrList, &objHdrList);
14079 
14080 		for(ahp = &new_pat->patgrp->arbhdr; *ahp; ahp = &(*ahp)->next)
14081 		  ;
14082 
14083 		for (n = 0; n < nHdrList; n++){
14084 		    char *hdrfld;
14085 		    char *hdrval;
14086 
14087 		    Tcl_ListObjGetElements(interp, objHdrList[n], &nHdrPair, &objHdrPair);
14088 		    if(nHdrPair != 2)
14089 		      continue;
14090 
14091 		    hdrfld = Tcl_GetStringFromObj(objHdrPair[0], NULL);
14092 		    hdrval = Tcl_GetStringFromObj(objHdrPair[1], NULL);
14093 
14094 		    if(hdrfld){
14095 			*ahp = (ARBHDR_S *) fs_get(sizeof(ARBHDR_S));
14096 			memset(*ahp, 0, sizeof(ARBHDR_S));
14097 
14098 			(*ahp)->field = cpystr(hdrfld);
14099 			if(hdrval){
14100 			    (*ahp)->p = string_to_pattern(hdrval);
14101 			}
14102 			else
14103 			  (*ahp)->isemptyval = 1;
14104 
14105 			ahp = &(*ahp)->next;
14106 		    }
14107 		}
14108 	    }
14109 	    else{
14110 		free_pat(&new_pat);
14111 		tcl_error++;
14112 	    }
14113 
14114 	    if(tstr)
14115 	      fs_give((void **) &tstr);
14116 
14117 	    if(tcl_error)
14118 	      return(TCL_ERROR);
14119 	}
14120 
14121 	if((new_pat->patgrp->inabook & (IAB_SPEC_YES | IAB_SPEC_NO)) == 0
14122 	   && new_pat->patgrp->abooks)
14123 	  free_pattern(&new_pat->patgrp->abooks);
14124 
14125 	if(new_pat->patgrp->fldr_type != FLDR_SPECIFIC && new_pat->patgrp->folder)
14126 	  free_pattern(&new_pat->patgrp->folder);
14127 
14128 	/* set up the action */
14129 	if(!(strcmp(rule, "filter")))
14130 	  new_pat->action->is_a_filter = 1;
14131 	else if(!(strcmp(rule, "role")))
14132 	  new_pat->action->is_a_role = 1;
14133 	else if(!(strcmp(rule, "score")))
14134 	  new_pat->action->is_a_score = 1;
14135 	else if(!(strcmp(rule, "indexcolor")))
14136 	  new_pat->action->is_a_incol = 1;
14137 	else{
14138 	    free_pat(&new_pat);
14139 	    return(TCL_ERROR);
14140 	}
14141 
14142 	for(i = 0; i < nAct; i++){
14143 	    Tcl_ListObjGetElements(interp, objAct[i], &nActEmnt, &objActEmnt);
14144 	    if(nActEmnt !=2){
14145 		free_pat(&new_pat);
14146 		return(TCL_ERROR);
14147 	    }
14148 
14149 	    actvar = Tcl_GetStringFromObj(objActEmnt[0], NULL);
14150 	    actval = Tcl_GetStringFromObj(objActEmnt[1], NULL);
14151 	    if(!actvar || !actval){
14152 		free_pat(&new_pat);
14153 		return(TCL_ERROR);
14154 	    }
14155 
14156 	    if(new_pat->action->is_a_filter && !(strcmp(actvar, "action"))){
14157 		if(!strcmp(actval, "delete"))
14158 		  new_pat->action->kill = 1;
14159 		else if(!strcmp(actval, "move"))
14160 		  new_pat->action->kill = 0;
14161 		else{
14162 		    free_pat(&new_pat);
14163 		    return(TCL_ERROR);
14164 		}
14165 	    }
14166 	    else if(new_pat->action->is_a_filter && !(strcmp(actvar, "folder"))){
14167 		tstr = cpystr(actval);
14168 		removing_leading_and_trailing_white_space(tstr);
14169 		if(!(*tstr)) fs_give((void **)&tstr);
14170 		new_pat->action->folder = string_to_pattern(tstr);
14171 		if(tstr) fs_give((void **)&tstr);
14172 	    }
14173 	    else if(new_pat->action->is_a_filter && !(strcmp(actvar, "moind"))){
14174 		if(!strcmp(actval, "1"))
14175 		  new_pat->action->move_only_if_not_deleted = 1;
14176 		else if(!strcmp(actval, "0"))
14177 		  new_pat->action->move_only_if_not_deleted = 0;
14178 		else{
14179 		    free_pat(&new_pat);
14180 		    return(TCL_ERROR);
14181 		}
14182 	    }
14183 	    else if(new_pat->action->is_a_incol && !(strcmp(actvar, "fg"))){
14184 		char  asciicolor[256];
14185 
14186 		if(ascii_colorstr(asciicolor, actval) == 0) {
14187 		    if(!new_pat->action->incol){
14188 			new_pat->action->incol = new_color_pair(asciicolor,NULL);
14189 		    }
14190 		    else
14191 		      snprintf(new_pat->action->incol->fg,
14192 			       sizeof(new_pat->action->incol->fg), "%s", asciicolor);
14193 		}
14194 	    }
14195 	    else if(new_pat->action->is_a_incol && !(strcmp(actvar, "bg"))){
14196 		char  asciicolor[256];
14197 
14198 		if(ascii_colorstr(asciicolor, actval) == 0) {
14199 		    if(!new_pat->action->incol){
14200 			new_pat->action->incol = new_color_pair(NULL, asciicolor);
14201 		    }
14202 		    else
14203 		      snprintf(new_pat->action->incol->bg,
14204 			       sizeof(new_pat->action->incol->bg), "%s", asciicolor);
14205 		}
14206 	    }
14207 	    else if(new_pat->action->is_a_score && !(strcmp(actvar, "scoreval"))){
14208 		long scoreval = (long) atoi(actval);
14209 
14210 		if(scoreval >= SCORE_MIN && scoreval <= SCORE_MAX)
14211 		  new_pat->action->scoreval = scoreval;
14212 	    }
14213 	    else if(new_pat->action->is_a_score && !(strcmp(actvar, "scorehdr"))){
14214 		HEADER_TOK_S *hdrtok;
14215 
14216 		if((hdrtok = stringform_to_hdrtok(actval)) != NULL)
14217 		  new_pat->action->scorevalhdrtok = hdrtok;
14218 	    }
14219 	    else{
14220 		free_pat(&new_pat);
14221 		return(TCL_ERROR);
14222 	    }
14223 	}
14224 
14225 	if(new_pat->action->is_a_filter && new_pat->action->kill && new_pat->action->folder)
14226 	  fs_give((void **)&new_pat->action->folder);
14227 	else if(new_pat->action->is_a_filter && new_pat->action->kill == 0 && new_pat->action->folder == 0){
14228 	    free_pat(&new_pat);
14229 	    Tcl_SetResult(interp, "No folder set for Move", TCL_VOLATILE);
14230 	    return(TCL_OK);
14231 	}
14232     }
14233 
14234     if(aflags & RS_RULE_EDIT)
14235       rv = edit_pattern(new_pat, rno, rflags);
14236     else if(aflags & RS_RULE_ADD)
14237       rv = add_pattern(new_pat, rflags);
14238     else if(aflags & RS_RULE_DELETE)
14239       rv = delete_pattern(rno, rflags);
14240     else if(aflags & RS_RULE_SHUFFUP)
14241       rv = shuffle_pattern(rno, 1, rflags);
14242     else if(aflags & RS_RULE_SHUFFDOWN)
14243       rv = shuffle_pattern(rno, -1, rflags);
14244     else
14245       rv = 1;
14246 
14247     return(rv ? TCL_ERROR : TCL_OK);
14248 }
14249 
14250 
14251 #if	0
14252 ADDRESS *
14253 peAEToAddress(AdrBk_Entry  *ae)
14254 {
14255     char    *list, *l1, *l2;
14256     int	     length;
14257     BuildTo  bldto;
14258     ADDRESS *addr = NULL;
14259 
14260     if(ae->tag == List){
14261 	length = 0;
14262 	for(l2 = ae->addr.list; *l2; l2++)
14263 	  length += (strlen(*l2) + 1);
14264 
14265 	list = (char *) fs_get(length + 1);
14266 	list[0] = '\0';
14267 	l1 = list;
14268 	for(l2 = ae->addr.list; *l2; l2++){
14269 	    if(l1 != list && l1-list < length+1)
14270 	      *l1++ = ',';
14271 
14272 	    strncpy(l1, *l2, length+1-(l1-list));
14273 	    l1 += strlen(l1);
14274 	}
14275 
14276 	list[length] = '\0';
14277 
14278 	bldto.type    = Str;
14279 	bldto.arg.str = list;
14280 	adr2 = expand_address(bldto, userdomain, localdomain,
14281 			      loop_detected, fcc, did_set,
14282 			      lcc, error, 1, simple_verify,
14283 			      mangled);
14284 
14285 	fs_give((void **) &list);
14286     }
14287     else if(ae->tag == Single){
14288 	if(strucmp(ae->addr.addr, a->mailbox)){
14289 	    bldto.type    = Str;
14290 	    bldto.arg.str = ae->addr.addr;
14291 	    adr2 = expand_address(bldto, userdomain,
14292 				  localdomain, loop_detected,
14293 				  fcc, did_set, lcc,
14294 				  error, 1, simple_verify,
14295 				  mangled);
14296 	}
14297 	else{
14298 	    /*
14299 	     * A loop within plain single entry is ignored.
14300 	     * Set up so later code thinks we expanded.
14301 	     */
14302 	    adr2          = mail_newaddr();
14303 	    adr2->mailbox = cpystr(ae->addr.addr);
14304 	    adr2->host    = cpystr(userdomain);
14305 	    adr2->adl     = cpystr(a->adl);
14306 	}
14307     }
14308 
14309     /*
14310      * Personal names:  If the expanded address has a personal
14311      * name and the address book entry is a list with a fullname,
14312      * tack the full name from the address book on in front.
14313      * This mainly occurs with a distribution list where the
14314      * list has a full name, and the first person in the list also
14315      * has a full name.
14316      *
14317      * This algorithm doesn't work very well if lists are
14318      * included within lists, but it's not clear what would
14319      * be better.
14320      */
14321     if(ae->fullname && ae->fullname[0]){
14322 	if(adr2->personal && adr2->personal[0]){
14323 	    if(ae->tag == List){
14324 		/* combine list name and existing name */
14325 		char *name;
14326 
14327 		if(!simple_verify){
14328 		    size_t l;
14329 		    l = strlen(adr2->personal) + strlen(ae->fullname) + 4;
14330 		    name = (char *)fs_get((l+1) * sizeof(char));
14331 		    snprintf(name, l+1, "%s -- %s", ae->fullname,
14332 			    adr2->personal);
14333 		    fs_give((void **)&adr2->personal);
14334 		    adr2->personal = name;
14335 		}
14336 	    }
14337 	    else{
14338 		/* replace with nickname fullname */
14339 		fs_give((void **)&adr2->personal);
14340 		adr2->personal = adrbk_formatname(ae->fullname,
14341 						  NULL, NULL);
14342 	    }
14343 	}
14344 	else{
14345 	    if(abe-p>tag != List || !simple_verify){
14346 		if(adr2->personal)
14347 		  fs_give((void **)&adr2->personal);
14348 
14349 		adr2->personal = adrbk_formatname(abe->fullname,
14350 						  NULL, NULL);
14351 	    }
14352 	}
14353     }
14354 
14355     return(addr);
14356 }
14357 
14358 
14359 
14360 char *
14361 peAEFcc(AdrBk_Entry *ae)
14362 {
14363     char *fcc = NULL;
14364 
14365     if(ae->fcc && ae->fcc[0]){
14366 
14367 	if(!strcmp(ae->fcc, "\"\""))
14368 	  fcc = cpystr("");
14369 	else
14370 	  fcc = cpystr(ae->fcc);
14371 
14372     }
14373     else if(ae->nickname && ae->nickname[0] &&
14374 	    (wps_global->fcc_rule == FCC_RULE_NICK ||
14375 	     wps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
14376 	/*
14377 	 * else if fcc-rule=fcc-by-nickname, use that
14378 	 */
14379 
14380 	fcc = cpystr(ae->nickname);
14381     }
14382 
14383     return(fcc);
14384 }
14385 #endif
14386 
14387 
14388 PINEFIELD *
peCustomHdrs(void)14389 peCustomHdrs(void)
14390 {
14391     extern PINEFIELD *parse_custom_hdrs(char **, CustomType);
14392 
14393     return(parse_custom_hdrs(wps_global->VAR_CUSTOM_HDRS, UseAsDef));
14394 }
14395 
14396 
14397 
14398 /*
14399  * PEClistCmd - Collection list editing tools
14400  */
14401 int
PEClistCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])14402 PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
14403 {
14404     char *err = "Unknown PEClist request";
14405 
14406     dprint((2, "PEClistCmd"));
14407 
14408     if(objc == 1){
14409 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
14410     }
14411     else{
14412 	char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
14413 
14414 	if(s1){
14415 	    if(objc == 3){ /* delete */
14416 	        if(!strcmp(s1, "delete")){
14417 		    int cl, i, n, deln;
14418 		    char **newl;
14419 		    CONTEXT_S *del_ctxt, *tmp_ctxt, *new_ctxt;
14420 
14421 		    if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14422 		      Tcl_SetResult(interp,
14423 				    "cledit malformed: first arg must be int",
14424 				    TCL_VOLATILE);
14425 		      return(TCL_ERROR);
14426 		    }
14427 		    for(i = 0, del_ctxt = wps_global->context_list;
14428 			del_ctxt && i < cl; i++, del_ctxt = del_ctxt->next);
14429 		    if(!del_ctxt) return(TCL_ERROR);
14430 		    for(n = 0; del_ctxt->var.v->current_val.l[n]; n++);
14431 		    n--;
14432 		    newl = (char **) fs_get((n + 1) * sizeof(char *));
14433 		    newl[n] = NULL;
14434 		    deln = del_ctxt->var.i;
14435 		    for(i = 0; del_ctxt->var.v->current_val.l[i]; i++){
14436 		      if(i < deln)
14437 			newl[i] = cpystr(del_ctxt->var.v->current_val.l[i]);
14438 		      else if(i > deln)
14439 			newl[i-1] = cpystr(del_ctxt->var.v->current_val.l[i]);
14440 		    }
14441 		    n = set_variable_list(del_ctxt->var.v - wps_global->vars,
14442 					  *newl ? newl : NULL, TRUE, Main);
14443 		    free_list_array(&newl);
14444 		    set_current_val(del_ctxt->var.v, TRUE, FALSE);
14445 		    if(n){
14446 		      Tcl_SetResult(interp,
14447 				    "Error saving changes",
14448 				    TCL_VOLATILE);
14449 		      return TCL_OK;
14450 		    }
14451 		    for(tmp_ctxt = del_ctxt->next; tmp_ctxt && tmp_ctxt->var.v ==
14452 			  del_ctxt->var.v; tmp_ctxt = tmp_ctxt->next)
14453 		      tmp_ctxt->var.i--;
14454 		    if((tmp_ctxt = del_ctxt->next) != NULL)
14455 		      tmp_ctxt->prev = del_ctxt->prev;
14456 		    if((tmp_ctxt = del_ctxt->prev) != NULL)
14457 		      tmp_ctxt->next= del_ctxt->next;
14458 		    if(!del_ctxt->prev && !del_ctxt->next){
14459 		        new_ctxt = new_context(del_ctxt->var.v->current_val.l[0], NULL);
14460 			wps_global->context_list = new_ctxt;
14461 			if(!new_ctxt->var.v)
14462 			  new_ctxt->var = del_ctxt->var;
14463 		    }
14464 		    else if(wps_global->context_list == del_ctxt){
14465 		        wps_global->context_list = del_ctxt->next;
14466 			if(!wps_global->context_list)
14467 			  return TCL_ERROR;  /* this shouldn't happen */
14468 		    }
14469 		    if(wps_global->context_last == del_ctxt)
14470 		      wps_global->context_last = NULL;
14471 		    if(wps_global->context_current == del_ctxt){
14472 		      strncpy(wps_global->cur_folder,
14473 			     wps_global->mail_stream->mailbox,
14474 			     sizeof(wps_global->cur_folder));
14475 		      wps_global->cur_folder[sizeof(wps_global->cur_folder)-1] = '\0';
14476 		      wps_global->context_current = wps_global->context_list;
14477 		    }
14478 		    del_ctxt->prev = NULL;
14479 		    del_ctxt->next = NULL;
14480 		    free_context(&del_ctxt);
14481 		    init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14482 				       wps_global->context_list);
14483 		    return TCL_OK;
14484 		}
14485 		else if(!strcmp(s1, "shuffdown")){
14486 		    int cl, i, shn, n;
14487 		    CONTEXT_S *sh_ctxt, *nsh_ctxt, *tctxt;
14488 		    char **newl, *tmpch;
14489 
14490 		    if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14491 		      Tcl_SetResult(interp,
14492 				    "cledit malformed: first arg must be int",
14493 				    TCL_VOLATILE);
14494 		      return(TCL_ERROR);
14495 		    }
14496 		    for(sh_ctxt = wps_global->context_list, i = 0;
14497 			sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
14498 		    if(!sh_ctxt || !sh_ctxt->next){
14499 		      Tcl_SetResult(interp,
14500 				    "invalid context list number",
14501 				    TCL_VOLATILE);
14502 		      return TCL_ERROR;
14503 		    }
14504 		    if(sh_ctxt->var.v == sh_ctxt->next->var.v){
14505 		        shn = sh_ctxt->var.i;
14506 			for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14507 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14508 			newl[n] = NULL;
14509 			for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
14510 			  if(i == shn)
14511 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
14512 			  else if(i == shn + 1)
14513 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
14514 			  else
14515 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14516 			}
14517 			n = set_variable_list(sh_ctxt->var.v - wps_global->vars,
14518 					      newl, TRUE, Main);
14519 			free_list_array(&newl);
14520 			set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14521 			if(n){
14522 			  Tcl_SetResult(interp,
14523 					"Error saving changes",
14524 					TCL_VOLATILE);
14525 			  return TCL_OK;
14526 			}
14527 			nsh_ctxt = sh_ctxt->next;
14528 			nsh_ctxt->var.i--;
14529 			sh_ctxt->var.i++;
14530 		    }
14531 		    else{
14532 		        nsh_ctxt = sh_ctxt->next;
14533 			shn = sh_ctxt->var.i;
14534 			tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
14535 			for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14536 			n--;
14537 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14538 			newl[n] = NULL;
14539 			for(i = 0; sh_ctxt->var.v->current_val.l[i+1]; i++)
14540 			  newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14541 			n = set_variable_list(sh_ctxt->var.v - wps_global->vars,
14542 					      newl, FALSE, Main);
14543 			free_list_array(&newl);
14544 			set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14545 			for(n = 0; nsh_ctxt->var.v->current_val.l[n]; n++);
14546 			n++;
14547 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14548 			newl[n] = NULL;
14549 			newl[0] = cpystr(nsh_ctxt->var.v->current_val.l[0]);
14550 			newl[1] = tmpch;
14551 			for(i = 2; nsh_ctxt->var.v->current_val.l[i-1]; i++)
14552 			  newl[i] = cpystr(nsh_ctxt->var.v->current_val.l[i-1]);
14553 			n = set_variable_list(nsh_ctxt->var.v - wps_global->vars,
14554 					      newl, TRUE, Main);
14555 			free_list_array(&newl);
14556 			set_current_val(nsh_ctxt->var.v, TRUE, FALSE);
14557 			sh_ctxt->var.v = nsh_ctxt->var.v;
14558 			sh_ctxt->var.i = 1;
14559 			/* this for loop assumes that there are only two variable lists,
14560 			 * folder-collections and news-collections, a little more will
14561 			 * have to be done if we want to accommodate for the INHERIT
14562 			 * option introduced in 4.30.
14563 			 */
14564 			for(tctxt = nsh_ctxt->next; tctxt; tctxt = tctxt->next)
14565 			  tctxt->var.i++;
14566 		    }
14567 		    if(sh_ctxt->prev) sh_ctxt->prev->next = nsh_ctxt;
14568 		    nsh_ctxt->prev = sh_ctxt->prev;
14569 		    sh_ctxt->next = nsh_ctxt->next;
14570 		    nsh_ctxt->next = sh_ctxt;
14571 		    sh_ctxt->prev = nsh_ctxt;
14572 		    if(sh_ctxt->next) sh_ctxt->next->prev = sh_ctxt;
14573 		    if(wps_global->context_list == sh_ctxt)
14574 		      wps_global->context_list = nsh_ctxt;
14575 		    init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14576 				       wps_global->context_list);
14577 		    return TCL_OK;
14578 		}
14579 		else if(!strcmp(s1, "shuffup")){
14580 		    int cl, i, shn, n;
14581 		    CONTEXT_S *sh_ctxt, *psh_ctxt, *tctxt;
14582 		    char **newl, *tmpch;
14583 
14584 		    if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14585 		      Tcl_SetResult(interp,
14586 				    "cledit malformed: first arg must be int",
14587 				    TCL_VOLATILE);
14588 		      return(TCL_ERROR);
14589 		    }
14590 		    for(sh_ctxt = wps_global->context_list, i = 0;
14591 			sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
14592 		    if(!sh_ctxt || !sh_ctxt->prev){
14593 		      Tcl_SetResult(interp,
14594 				    "invalid context list number",
14595 				    TCL_VOLATILE);
14596 		      return TCL_ERROR;
14597 		    }
14598 		    if(sh_ctxt->var.v == sh_ctxt->prev->var.v){
14599 		        shn = sh_ctxt->var.i;
14600 			for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14601 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14602 			newl[n] = NULL;
14603 			for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
14604 			  if(i == shn)
14605 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
14606 			  else if(i == shn - 1)
14607 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
14608 			  else
14609 			    newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14610 			}
14611 			i = set_variable_list(sh_ctxt->var.v - wps_global->vars,
14612 					      newl, TRUE, Main);
14613 			free_list_array(&newl);
14614 			set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14615 			if(i){
14616 			  Tcl_SetResult(interp,
14617 					"Error saving changes",
14618 					TCL_VOLATILE);
14619 			  return TCL_OK;
14620 			}
14621 			psh_ctxt = sh_ctxt->prev;
14622 			psh_ctxt->var.i++;
14623 			sh_ctxt->var.i--;
14624 		    }
14625 		    else{
14626 		        psh_ctxt = sh_ctxt->prev;
14627 			shn = sh_ctxt->var.i;
14628 			tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
14629 			for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14630 			n--;
14631 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14632 			newl[n] = NULL;
14633 			for(i = 1; sh_ctxt->var.v->current_val.l[i]; i++)
14634 			  newl[i-1] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14635 			i = set_variable_list(sh_ctxt->var.v - wps_global->vars,
14636 					      newl, FALSE, Main);
14637 			free_list_array(&newl);
14638 			if(i){
14639 			  Tcl_SetResult(interp,
14640 					"Error saving changes",
14641 					TCL_VOLATILE);
14642 			  return TCL_OK;
14643 			}
14644 			set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14645 			for(n = 0; psh_ctxt->var.v->current_val.l[n]; n++);
14646 			n++;
14647 			newl = (char **) fs_get((n + 1) * sizeof(char *));
14648 			newl[n] = NULL;
14649 			for(i = 0; psh_ctxt->var.v->current_val.l[i+1]; i++)
14650 			  newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i]);
14651 			newl[i++] = tmpch;
14652 			newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i-1]);
14653 			i = set_variable_list(psh_ctxt->var.v - wps_global->vars,
14654 					      newl, TRUE, Main);
14655 			free_list_array(&newl);
14656 			if(i){
14657 			  Tcl_SetResult(interp,
14658 					"Error saving changes",
14659 					TCL_VOLATILE);
14660 			  return TCL_OK;
14661 			}
14662 			set_current_val(psh_ctxt->var.v, TRUE, FALSE);
14663 			for(tctxt = sh_ctxt->next ; tctxt; tctxt = tctxt->next)
14664 			  tctxt->var.i--;
14665 			sh_ctxt->var.v = psh_ctxt->var.v;
14666 			sh_ctxt->var.i = n - 2;
14667 			/* There MUST be at least 2 collections in the list */
14668 			psh_ctxt->var.i++;
14669 		    }
14670 		    if(sh_ctxt->next) sh_ctxt->next->prev = psh_ctxt;
14671 		    psh_ctxt->next = sh_ctxt->next;
14672 		    sh_ctxt->prev = psh_ctxt->prev;
14673 		    psh_ctxt->prev = sh_ctxt;
14674 		    sh_ctxt->next = psh_ctxt;
14675 		    if(sh_ctxt->prev) sh_ctxt->prev->next = sh_ctxt;
14676 		    if(wps_global->context_list == psh_ctxt)
14677 		      wps_global->context_list = sh_ctxt;
14678 		    init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14679 				       wps_global->context_list);
14680 		    return TCL_OK;
14681 		}
14682 	    }
14683 	    else if(objc == 7){
14684 	        if(!strcmp(s1, "edit") || !strcmp(s1, "add")){
14685 		    int cl, quotes_needed = 0, i, add = 0, n = 0;
14686 		    char *nick, *server, *path, *view,
14687 		          context_buf[MAILTMPLEN*4], **newl;
14688 		    CONTEXT_S *new_ctxt, *tmp_ctxt;
14689 
14690 		    if(!strcmp(s1, "add")) add = 1;
14691 
14692 		    if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14693 		      Tcl_SetResult(interp,
14694 				    "cledit malformed: first arg must be int",
14695 				    TCL_VOLATILE);
14696 		      return(TCL_ERROR);
14697 		    }
14698 		    if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))){
14699 		      Tcl_SetResult(interp,
14700 				    "Error1",
14701 				    TCL_VOLATILE);
14702 		      return TCL_ERROR;
14703 		    }
14704 		    if(!(server = Tcl_GetStringFromObj(objv[4], NULL))){
14705 		      Tcl_SetResult(interp,
14706 				    "Error2",
14707 				    TCL_VOLATILE);
14708 		      return TCL_ERROR;
14709 		    }
14710 		    if(!(path = Tcl_GetStringFromObj(objv[5], NULL))){
14711 		      Tcl_SetResult(interp,
14712 				    "Error3",
14713 				    TCL_VOLATILE);
14714 		      return TCL_ERROR;
14715 		    }
14716 		    if(!(view = Tcl_GetStringFromObj(objv[6], NULL))){
14717 		      Tcl_SetResult(interp,
14718 				    "Error4",
14719 				    TCL_VOLATILE);
14720 		      return TCL_ERROR;
14721 		    }
14722 		    removing_leading_and_trailing_white_space(nick);
14723 		    removing_leading_and_trailing_white_space(server);
14724 		    removing_leading_and_trailing_white_space(path);
14725 		    removing_leading_and_trailing_white_space(view);
14726 		    if(strchr(nick, ' '))
14727 		      quotes_needed = 1;
14728 		    if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
14729 		       MAILTMPLEN * 4 - 20) { /* for good measure */
14730 		      Tcl_SetResult(interp,
14731 				    "info too long",
14732 				    TCL_VOLATILE);
14733 
14734 		      return TCL_ERROR;
14735 		    }
14736 		    if(3 + strlen(nick) + strlen(server) + strlen(path) +
14737 		       strlen(view) > MAILTMPLEN + 4){
14738 		       Tcl_SetResult(interp,
14739 				     "collection fields too long",
14740 				     TCL_VOLATILE);
14741 		       return(TCL_OK);
14742 		    }
14743 		    snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
14744 			    "\"" : "", nick, quotes_needed ? "\"" : "",
14745 			    strlen(nick) ? " " : "",
14746 			    server, path, view);
14747 		    new_ctxt = new_context(context_buf, NULL);
14748 		    if(!add){
14749 		        for(tmp_ctxt = wps_global->context_list, i = 0;
14750 			   tmp_ctxt && i < cl; i++, tmp_ctxt = tmp_ctxt->next);
14751 			if(!tmp_ctxt){
14752 			    Tcl_SetResult(interp,
14753 					  "invalid context list number",
14754 					  TCL_VOLATILE);
14755 			    return TCL_ERROR;
14756 			}
14757 			new_ctxt->next = tmp_ctxt->next;
14758 			new_ctxt->prev = tmp_ctxt->prev;
14759 			if(tmp_ctxt->prev && tmp_ctxt->prev->next == tmp_ctxt)
14760 			    tmp_ctxt->prev->next = new_ctxt;
14761 			if(tmp_ctxt->next && tmp_ctxt->next->prev == tmp_ctxt)
14762 			    tmp_ctxt->next->prev = new_ctxt;
14763 			if(wps_global->context_list == tmp_ctxt)
14764 			    wps_global->context_list = new_ctxt;
14765 			if(wps_global->context_current == tmp_ctxt){
14766 			    strncpy(wps_global->cur_folder,
14767 				   wps_global->mail_stream->mailbox,
14768 				   sizeof(wps_global->cur_folder));
14769 			    wps_global->cur_folder[sizeof(wps_global->cur_folder)-1] = '\0';
14770 			    wps_global->context_current = new_ctxt;
14771 			}
14772 			if(wps_global->context_last == tmp_ctxt)
14773 			    wps_global->context_last = new_ctxt;
14774 			new_ctxt->var = tmp_ctxt->var;
14775 			tmp_ctxt->next = tmp_ctxt->prev = NULL;
14776 			free_context(&tmp_ctxt);
14777 		    }
14778 		    else {
14779 		        for(tmp_ctxt = wps_global->context_list;
14780 			    tmp_ctxt->next; tmp_ctxt = tmp_ctxt->next);
14781 			new_ctxt->prev = tmp_ctxt;
14782 			tmp_ctxt->next = new_ctxt;
14783 			new_ctxt->var.v = tmp_ctxt->var.v;
14784 			new_ctxt->var.i = tmp_ctxt->var.i + 1;
14785 		    }
14786 		    if(!new_ctxt->var.v){
14787 		      Tcl_SetResult(interp,
14788 				    "Error5",
14789 				    TCL_VOLATILE);
14790 		      return TCL_ERROR;
14791 		    }
14792 		    for(n = 0; new_ctxt->var.v->current_val.l[n]; n++);
14793 		    if(add) n++;
14794 		    newl = (char **) fs_get((n + 1) * sizeof(char *));
14795 		    newl[n] = NULL;
14796 		    for(n = 0; new_ctxt->var.v->current_val.l[n]; n++)
14797 		        newl[n] = (n == new_ctxt->var.i)
14798 			  ? cpystr(context_buf)
14799 			  : cpystr(new_ctxt->var.v->current_val.l[n]);
14800 		    if(add) newl[n++] = cpystr(context_buf);
14801 		    n = set_variable_list(new_ctxt->var.v - wps_global->vars,
14802 					  newl, TRUE, Main);
14803 		    free_list_array(&newl);
14804 		    set_current_val(new_ctxt->var.v, TRUE, FALSE);
14805 		    init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14806 				       wps_global->context_list);
14807 		    if(n){
14808 		      Tcl_SetResult(interp,
14809 				    "Error saving changes",
14810 				    TCL_VOLATILE);
14811 		      return TCL_OK;
14812 		    }
14813 		    return TCL_OK;
14814 
14815 		}
14816 	    }
14817 	}
14818     }
14819     Tcl_SetResult(interp, err, TCL_STATIC);
14820     return(TCL_ERROR);
14821 }
14822 
14823 
14824 /*
14825  * peTakeaddr - Take Address
14826  */
14827 int
peTakeaddr(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)14828 peTakeaddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
14829 {
14830     TA_S     *talist = NULL, *current, *head;
14831     Tcl_Obj  *itemObj, *secObj = NULL, *resObj = NULL;
14832     int anum = 0;
14833 
14834     mn_set_cur(sp_msgmap(wps_global->mail_stream), peMessageNumber(uid));
14835 
14836     if(set_up_takeaddr('a', wps_global, sp_msgmap(wps_global->mail_stream),
14837 		       &talist, &anum, TA_NOPROMPT, NULL) < 0
14838                        || (talist == NULL)){
14839         Tcl_SetResult(interp,
14840 		      "Take address failed to set up",
14841 		      TCL_VOLATILE);
14842 	return(TCL_ERROR);
14843     }
14844 
14845     for(head = talist ; head->prev; head = head->prev);
14846     /*
14847      * Return value will be of the form:
14848      *  {
14849      *   { "line to print",
14850      *     {"personal", "mailbox", "host"}         # addr
14851      *     {"nick", "fullname", "fcc", "comment"}  # suggested
14852      *   }
14853      *   ...
14854      *  }
14855      *
14856      *  The two list items will be empty if that line is
14857      *  just informational.
14858      */
14859     itemObj = Tcl_NewListObj(0, NULL);
14860     for(current = head; current ; current = current->next){
14861         if(current->skip_it && !current->print) continue;
14862 	secObj = Tcl_NewListObj(0, NULL);
14863 	if(Tcl_ListObjAppendElement(interp, secObj,
14864 			   Tcl_NewStringObj(current->strvalue,-1)) != TCL_OK)
14865 	    return(TCL_ERROR);
14866 	resObj = Tcl_NewListObj(0, NULL);
14867 	/* append the address information */
14868 	if(current->addr && !current->print){
14869 	    if(Tcl_ListObjAppendElement(interp, resObj,
14870 				  Tcl_NewStringObj(current->addr->personal
14871 						   ? current->addr->personal
14872 						   : "", -1)) != TCL_OK)
14873 	        return(TCL_ERROR);
14874 	    if(Tcl_ListObjAppendElement(interp, resObj,
14875 				     Tcl_NewStringObj(current->addr->mailbox
14876 						      ? current->addr->mailbox
14877 						      : "", -1)) != TCL_OK)
14878 	        return(TCL_ERROR);
14879 	    if(Tcl_ListObjAppendElement(interp, resObj,
14880 					Tcl_NewStringObj(current->addr->host
14881 							 ? current->addr->host
14882 							 : "", -1)) != TCL_OK)
14883 	        return(TCL_ERROR);
14884 	}
14885 	if(Tcl_ListObjAppendElement(interp, secObj,
14886 				    resObj) != TCL_OK)
14887 	    return(TCL_ERROR);
14888 	resObj = Tcl_NewListObj(0, NULL);
14889 	/* append the suggested possible entries */
14890 	if(!current->print
14891 	   && (current->nickname || current->fullname
14892 	       || current->fcc || current->comment)){
14893 	    if(Tcl_ListObjAppendElement(interp, resObj,
14894 					Tcl_NewStringObj(current->nickname
14895 							 ? current->nickname
14896 							 : "", -1)) != TCL_OK)
14897 	        return(TCL_ERROR);
14898 	    if(Tcl_ListObjAppendElement(interp, resObj,
14899 					Tcl_NewStringObj(current->fullname
14900 							 ? current->fullname
14901 							 : "", -1)) != TCL_OK)
14902 	        return(TCL_ERROR);
14903 	    if(Tcl_ListObjAppendElement(interp, resObj,
14904 					Tcl_NewStringObj(current->fcc
14905 							 ? current->fcc
14906 							 : "", -1)) != TCL_OK)
14907 	        return(TCL_ERROR);
14908 	    if(Tcl_ListObjAppendElement(interp, resObj,
14909 					Tcl_NewStringObj(current->comment
14910 							 ? current->comment
14911 							 : "", -1)) != TCL_OK)
14912 	        return(TCL_ERROR);
14913 	}
14914 	if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
14915 	    return(TCL_ERROR);
14916 	if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
14917 				    secObj) != TCL_OK)
14918 	    return(TCL_ERROR);
14919     }
14920 
14921     free_talines(&talist);
14922     return(TCL_OK);
14923 }
14924 
14925 
14926 /*
14927  * peTakeFrom - Take only From Address
14928  */
14929 int
peTakeFrom(Tcl_Interp * interp,imapuid_t uid,int objc,Tcl_Obj ** objv)14930 peTakeFrom(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
14931 {
14932     char     *err = NULL;
14933     Tcl_Obj  *objItem;
14934     ADDRESS  *ap;
14935     ENVELOPE *env;
14936     long      rawno;
14937 
14938     /*
14939      * Return value will be of the form:
14940      *  {
14941      *   { "line to print",
14942      *     {"personal", "mailbox", "host"}         # addr
14943      *     {"nick", "fullname", "fcc", "comment"}  # suggested
14944      *   }
14945      *   ...
14946      *  }
14947      *
14948      *  The two list items will be empty if that line is
14949      *  just informational.
14950      */
14951 
14952     if(uid){
14953 	if((env = pine_mail_fetchstructure(wps_global->mail_stream,
14954 					   rawno = peSequenceNumber(uid),
14955 					   NULL))){
14956 	    /* append the address information */
14957 	    for(ap = env->from; ap; ap = ap->next){
14958 		objItem = Tcl_NewListObj(0, NULL);
14959 		/* append EMPTY "line to print" */
14960 		if(Tcl_ListObjAppendElement(interp, objItem, Tcl_NewStringObj("",-1)) != TCL_OK)
14961 		  return(TCL_ERROR);
14962 
14963 		/* append address info */
14964 		peAppListF(interp, objItem, "%s%s%s",
14965 			   ap->personal ? (char *) rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf, SIZEOF_20KBUF, ap->personal) : "",
14966 			   ap->mailbox ? ap->mailbox : "",
14967 			   ap->host ? ap->host : "");
14968 
14969 		/* append suggested info */
14970 		peAddSuggestedContactInfo(interp, objItem, ap);
14971 
14972 		if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objItem) != TCL_OK)
14973 		  return(TCL_ERROR);
14974 	    }
14975 
14976 	    return(TCL_OK);
14977 	}
14978 	else
14979 	  err = wps_global->last_error;
14980     }
14981     else
14982       err = "Invalid UID";
14983 
14984     return(TCL_ERROR);
14985 }
14986 
14987 
14988 int
peAddSuggestedContactInfo(Tcl_Interp * interp,Tcl_Obj * lobjp,ADDRESS * addr)14989 peAddSuggestedContactInfo(Tcl_Interp *interp, Tcl_Obj *lobjp, ADDRESS *addr)
14990 {
14991     char *nick = NULL, *full = NULL, *fcc = NULL, *comment = NULL;
14992 
14993     get_contactinfo_from_addr(addr, &nick, &full, &fcc, &comment);
14994 
14995     peAppListF(interp, lobjp, "%s%s%s%s",
14996 	       nick ? nick : "",
14997 	       full ? full : "",
14998 	       fcc ? fcc : "",
14999 	       comment ? comment : "");
15000 
15001     if(nick)
15002       fs_give((void **) &nick);
15003 
15004     if(full)
15005       fs_give((void **) &full);
15006 
15007     if(fcc)
15008       fs_give((void **) &fcc);
15009 
15010     if(comment)
15011       fs_give((void **) &comment);
15012 
15013     return 0;
15014 }
15015 
15016 
15017 /* * * * * * * * * Status message ring management * * * * * * * * * * * * */
15018 
15019 STATMSG_S *
sml_newmsg(int priority,char * text)15020 sml_newmsg(int priority, char *text)
15021 {
15022     static     long id = 1;
15023     STATMSG_S *smp;
15024 
15025     smp = (STATMSG_S *) fs_get(sizeof(STATMSG_S));
15026     memset(smp, 0, sizeof(STATMSG_S));
15027     smp->id = id++;
15028     smp->posted = time(0);
15029     smp->type = priority;
15030     smp->text = cpystr(text);
15031     return(smp);
15032 }
15033 
15034 
15035 void
sml_addmsg(int priority,char * text)15036 sml_addmsg(int priority, char *text)
15037 {
15038     STATMSG_S *smp = sml_newmsg(priority, text);
15039 
15040     if(peStatList){
15041 	smp->next = peStatList;
15042 	peStatList = smp;
15043     }
15044     else
15045       peStatList = smp;
15046 }
15047 
15048 
15049 char **
sml_getmsgs(void)15050 sml_getmsgs(void)
15051 {
15052     int n;
15053     STATMSG_S *smp;
15054     char **retstrs = NULL, **tmpstrs;
15055 
15056     for(n = 0, smp = peStatList; smp && !smp->seen; n++, smp = smp->next)
15057       ;
15058 
15059     if(n == 0) return NULL;
15060     retstrs = (char **)fs_get((n+1)*sizeof(char *));
15061     for(tmpstrs = retstrs, smp = peStatList; smp && !smp->seen;	smp = smp->next){
15062         *tmpstrs = smp->text;
15063 	tmpstrs++;
15064     }
15065 
15066     *tmpstrs = NULL;
15067     return(retstrs);
15068 }
15069 
15070 
15071 char *
sml_getmsg(void)15072 sml_getmsg(void)
15073 {
15074     return(peStatList ? peStatList->text : "");
15075 }
15076 
15077 void
sml_seen(void)15078 sml_seen(void)
15079 {
15080     STATMSG_S *smp;
15081 
15082     for(smp = peStatList; smp; smp = smp->next)
15083       smp->seen = 1;
15084 }
15085 
15086 
15087 
15088 /* * * * * * * * * LDAP Support Routines  * * * * * * * * * * * */
15089 
15090 
15091 /*
15092  * PELdapCmd - LDAP TCL interface
15093  */
15094 int
PELdapCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])15095 PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
15096 {
15097 #ifndef ENABLE_LDAP
15098     char *err = "Call to PELdap when LDAP not enabled";
15099 #else
15100     char *err = "Unknown PELdap request";
15101     char *s1;
15102 
15103     dprint((2, "PELdapCmd"));
15104 
15105     if(objc == 1){
15106 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15107 	Tcl_SetResult(interp, err, TCL_STATIC);
15108 	return(TCL_ERROR);
15109     }
15110     s1 = Tcl_GetStringFromObj(objv[1], NULL);
15111 
15112     if(s1){
15113         int qn;
15114 	if(!strcmp(s1, "directories")){
15115 	    int          i;
15116 	    LDAP_SERV_S *info;
15117 	    Tcl_Obj     *secObj;
15118 
15119 	    if(objc != 2){
15120 	        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15121 		Tcl_SetResult(interp, err, TCL_STATIC);
15122 		return(TCL_ERROR);
15123 	    }
15124 	    if(wps_global->VAR_LDAP_SERVERS){
15125 		for(i = 0; wps_global->VAR_LDAP_SERVERS[i] &&
15126 		      wps_global->VAR_LDAP_SERVERS[i][0]; i++){
15127 		    info = break_up_ldap_server(wps_global->VAR_LDAP_SERVERS[i]);
15128 		    secObj = Tcl_NewListObj(0, NULL);
15129 		    if(Tcl_ListObjAppendElement(interp, secObj,
15130 						Tcl_NewStringObj(info->nick ? info->nick
15131 								 : "", -1)) != TCL_OK)
15132 		      return(TCL_ERROR);
15133 		    if(Tcl_ListObjAppendElement(interp, secObj,
15134 						Tcl_NewStringObj(info->serv ? info->serv
15135 								 : "", -1)) != TCL_OK)
15136 		      return(TCL_ERROR);
15137 
15138 		    if(info)
15139 		      free_ldap_server_info(&info);
15140 		    if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15141 						secObj) != TCL_OK)
15142 		      return(TCL_ERROR);
15143 		}
15144 	    }
15145 	    else
15146 	      Tcl_SetResult(interp, "", TCL_STATIC);
15147 
15148 	    return(TCL_OK);
15149 	}
15150 	else if(!strcmp(s1, "query")){
15151 	    int               dir;
15152 	    char             *srchstr, *filtstr;
15153 	    LDAP_CHOOSE_S    *winning_e = NULL;
15154 	    LDAP_SERV_RES_S  *results = NULL;
15155 	    WP_ERR_S          wp_err;
15156 	    CUSTOM_FILT_S    *filter = NULL;
15157 
15158 	    if(objc != 5){
15159 	        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15160 		Tcl_SetResult(interp, err, TCL_STATIC);
15161 		return(TCL_ERROR);
15162 	    }
15163 	    if(Tcl_GetIntFromObj(interp, objv[2], &dir) == TCL_ERROR){
15164 	        Tcl_SetResult(interp,
15165 			      "PELdap results malformed: first arg must be int",
15166 			      TCL_VOLATILE);
15167 		return(TCL_ERROR);
15168 	    }
15169 	    wpldap_global->query_no++;
15170 	    if(wpldap_global->ldap_search_list){
15171 	        wpldap_global->ldap_search_list =
15172 		  free_wpldapres(wpldap_global->ldap_search_list);
15173 	    }
15174 	    srchstr = Tcl_GetStringFromObj(objv[3], NULL);
15175 	    filtstr = Tcl_GetStringFromObj(objv[4], NULL);
15176 	    if(!srchstr) return(TCL_ERROR);
15177 	    if(!filtstr) return(TCL_ERROR);
15178 	    if(*filtstr){
15179 	        filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
15180 		filter->filt = cpystr(filtstr);
15181 		filter->combine = 0;
15182 	    }
15183 	    memset(&wp_err, 0, sizeof(wp_err));
15184 	    ldap_lookup_all(srchstr, dir, 0, AlwaysDisplay, filter, &winning_e,
15185 			    &wp_err, &results);
15186 	    if(filter){
15187 	      fs_give((void **)&filter->filt);
15188 	      fs_give((void **)&filter);
15189 	    }
15190 	    Tcl_SetResult(interp, int2string(wpldap_global->ldap_search_list
15191 					     ? wpldap_global->query_no : 0),
15192 			  TCL_VOLATILE);
15193 	    return(TCL_OK);
15194 	}
15195 	/*
15196 	 * First argument has always got to be the query number for now.
15197 	 * Might need to rething that when setting up queries.
15198 	 */
15199 	if(objc == 2){
15200 	    Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15201 	    Tcl_SetResult(interp, err, TCL_STATIC);
15202 	    return(TCL_ERROR);
15203 	}
15204 	if(Tcl_GetIntFromObj(interp, objv[2], &qn) == TCL_ERROR){
15205 	    Tcl_SetResult(interp,
15206 			  "PELdap results malformed: first arg must be int",
15207 			  TCL_VOLATILE);
15208 	    return(TCL_ERROR);
15209 	}
15210 	if(qn != wpldap_global->query_no){
15211 	    Tcl_SetResult(interp,
15212 			  "Query is no longer valid", TCL_VOLATILE);
15213 	    return(TCL_ERROR);
15214 	}
15215 	if(objc == 3){
15216 	    if(!strcmp(s1, "results")){
15217 	      return(peLdapQueryResults(interp));
15218 	    }
15219 	}
15220 	else if(objc == 4){
15221 	    if(!strcmp(s1, "ldapext")){
15222 	        /*
15223 		 *  Returns a list of the form:
15224 		 *  {"dn" {{attrib {val, ...}}, ...}}
15225 		 */
15226 	        char *whichrec = Tcl_GetStringFromObj(objv[3], NULL);
15227 		char *tmpstr, *tmp, *tmp2, *a;
15228 		struct berval **vals;
15229 		WPLDAPRES_S     *curres;
15230 		LDAP_CHOOSE_S   *winning_e = NULL;
15231 		LDAP_SERV_RES_S *trl;
15232 		Tcl_Obj     *secObj = NULL, *resObj = NULL, *itemObj;
15233 		BerElement *ber;
15234 		LDAPMessage *e;
15235 		int i, j, whichi, whichj;
15236 
15237 		if(whichrec == NULL){
15238 		  Tcl_SetResult(interp, "Ldap ldapext error 1", TCL_VOLATILE);
15239 		  return TCL_ERROR;
15240 		}
15241 		tmpstr = cpystr(whichrec);
15242 		tmp = tmpstr;
15243 		for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15244 		if(*tmp2 != '.'){
15245 		  Tcl_SetResult(interp, "Ldap ldapext error 2", TCL_VOLATILE);
15246 		  return TCL_ERROR;
15247 		}
15248 		*tmp2 = '\0';
15249 		whichi = atoi(tmp);
15250 		*tmp2 = '.';
15251 		tmp2++;
15252 		for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15253 		if(*tmp2 != '\0'){
15254 		  Tcl_SetResult(interp, "Ldap ldapext error 3", TCL_VOLATILE);
15255 		  return TCL_ERROR;
15256 		}
15257 		whichj = atoi(tmp);
15258 		fs_give((void **)&tmpstr);
15259 		for(curres = wpldap_global->ldap_search_list, i = 0;
15260 		    i < whichi && curres; i++, curres = curres->next);
15261 		if(!curres){
15262 		  Tcl_SetResult(interp, "Ldap ldapext error 4", TCL_VOLATILE);
15263 		  return TCL_ERROR;
15264 		}
15265 		for(trl = curres->reslist, j = 0; trl; trl = trl->next){
15266 		    for(e = ldap_first_entry(trl->ld, trl->res);
15267 			e != NULL && j < whichj;
15268 			e = ldap_next_entry(trl->ld, e), j++);
15269 		    if(e != NULL && j == whichj)
15270 		      break;
15271 		}
15272 		if(e == NULL || trl == NULL){
15273 		  Tcl_SetResult(interp, "Ldap ldapext error 5", TCL_VOLATILE);
15274 		  return TCL_ERROR;
15275 		}
15276 		winning_e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
15277 		winning_e->ld        = trl->ld;
15278 		winning_e->selected_entry = e;
15279 		winning_e->info_used = trl->info_used;
15280 		winning_e->serv      = trl->serv;
15281 		a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
15282 		if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15283 					    Tcl_NewStringObj(a ? a : "", -1)) != TCL_OK)
15284 		  return(TCL_ERROR);
15285 		if(a)
15286 		  our_ldap_dn_memfree(a);
15287 
15288 		itemObj = Tcl_NewListObj(0, NULL);
15289 		for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
15290 		    a != NULL;
15291 		    a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
15292 		    if(a && *a){
15293 		        secObj = Tcl_NewListObj(0, NULL);
15294 			if(Tcl_ListObjAppendElement(interp, secObj,
15295 						    Tcl_NewStringObj(ldap_translate(a,
15296 						  winning_e->info_used), -1)) != TCL_OK)
15297 			  return(TCL_ERROR);
15298 			resObj = Tcl_NewListObj(0, NULL);
15299 			vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
15300 			if(vals){
15301 			    for(i = 0; vals[i]; i++){
15302 			        if(Tcl_ListObjAppendElement(interp, resObj,
15303 					  Tcl_NewStringObj(vals[i]->bv_val, -1)) != TCL_OK)
15304 				  return(TCL_ERROR);
15305 			    }
15306 			    ldap_value_free_len(vals);
15307 			    if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15308 			      return(TCL_ERROR);
15309 			}
15310 			if(!strcmp(a,"objectclass")){
15311 			    if(Tcl_ListObjAppendElement(interp, secObj,
15312 				       Tcl_NewStringObj("objectclass", -1)) != TCL_OK)
15313 			      return(TCL_ERROR);
15314 			}
15315 			if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK)
15316 			  return(TCL_ERROR);
15317 		    }
15318 		    our_ldap_memfree(a);
15319 		}
15320 
15321 		if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15322 					    itemObj) != TCL_OK)
15323 		  return(TCL_ERROR);
15324 
15325 		fs_give((void **)&winning_e);
15326 		return(TCL_OK);
15327 	    }
15328 	}
15329 	else if(objc == 6){
15330 	  if(!strcmp(s1, "setaddrs")){
15331 	      char            *listset = Tcl_GetStringFromObj(objv[3], NULL);
15332 	      char            *addrstr = Tcl_GetStringFromObj(objv[4], NULL);
15333 	      char            *tmp, *tmp2, *tmplistset, was_char, *ret_to,
15334 		              *tmpaddrstr;
15335 	      int            **lset, noreplace = 0;
15336 	      ADDRESS         *adr = NULL, *curadr, *prevadr, *newadr,
15337 		              *curnewadr, *newadrs;
15338 	      int              curi, i, j, numsrchs, numset, setit;
15339 	      LDAP_CHOOSE_S   *tres;
15340 	      LDAP_SERV_RES_S *trl;
15341 	      WPLDAPRES_S     *curres;
15342 	      LDAPMessage     *e;
15343 	      RFC822BUFFER     rbuf;
15344 	      size_t           len;
15345 
15346 	      if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){
15347 		  Tcl_SetResult(interp,
15348 				"PELdap results malformed: first arg must be int",
15349 				TCL_VOLATILE);
15350 		  return(TCL_ERROR);
15351 	      }
15352 	      if(listset == NULL || addrstr == NULL) return TCL_ERROR;
15353 	      tmpaddrstr = cpystr(addrstr);
15354 
15355 	      if(!noreplace){
15356 		  mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
15357 		  rfc822_parse_adrlist(&adr, tmpaddrstr, "@");
15358 		  mail_parameters(NIL, SET_PARSEPHRASE, NULL);
15359 	      }
15360 
15361 	      tmplistset = cpystr(listset);
15362 	      for(curres = wpldap_global->ldap_search_list, numsrchs = 0;
15363 		  curres; curres = curres->next, numsrchs++);
15364 	      lset = (int **)fs_get((numsrchs+1)*sizeof(int *));
15365 	      for(i = 0; i < numsrchs; i++){
15366 		  for(tmp = tmplistset, numset = 0; *tmp;){
15367 		      for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15368 		      if(*tmp2 != '.'){
15369 			Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE);
15370 			return TCL_ERROR;
15371 		      }
15372 		      if(atoi(tmp) ==  i) numset++;
15373 		      tmp2++;
15374 		      for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15375 		      if(*tmp2 != ',' && *tmp2 != '\0'){
15376 			Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE);
15377 			return TCL_ERROR;
15378 		      }
15379 		      if(*tmp2) tmp2++;
15380 		      tmp = tmp2;
15381 		  }
15382 		  lset[i] = (int *)fs_get((numset+1)*sizeof(int));
15383 		  for(tmp = tmplistset, j = 0; *tmp && j < numset;){
15384 		      setit = 0;
15385 		      for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15386 		      if(*tmp2 != '.'){
15387 			Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE);
15388 			return TCL_ERROR;
15389 		      }
15390 		      *tmp2 = '\0';
15391 		      if(atoi(tmp) ==  i) setit++;
15392 		      *tmp2 = '.';
15393 		      tmp2++;
15394 		      for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15395 		      if(*tmp2 != ',' && *tmp2 != '\0'){
15396 			Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE);
15397 			return TCL_ERROR;
15398 		      }
15399 		      if(setit){
15400 			  was_char = *tmp2;
15401 			  *tmp2 = '\0';
15402 			  lset[i][j++] = atoi(tmp);
15403 			  *tmp2 = was_char;
15404 		      }
15405 		      if(*tmp2) tmp2++;
15406 		      tmp = tmp2;
15407 		  }
15408 		  lset[i][j] = -1;
15409 	      }
15410 	      lset[i] = NULL;
15411 	      for(i = 0, curres = wpldap_global->ldap_search_list;
15412 		  i < numsrchs && curres; i++, curres = curres->next){
15413 		  prevadr = NULL;
15414 		  for(curadr = adr; curadr; curadr = curadr->next){
15415 		      if(strcmp(curadr->mailbox, curres->str) == 0
15416 			 && curadr->host && *curadr->host == '@')
15417 			break;
15418 		      prevadr = curadr;
15419 		  }
15420 		  if(!curadr && !noreplace){
15421 		    Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE);
15422 		    return TCL_ERROR;
15423 		  }
15424 		  newadrs = newadr = curnewadr = NULL;
15425 		  for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){
15426 		  for(e = ldap_first_entry(trl->ld, trl->res);
15427 		      e != NULL && lset[i][curi] != -1;
15428 		      e = ldap_next_entry(trl->ld, e), j++){
15429 		      if(j == lset[i][curi]){
15430 			  tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
15431 			  tres->ld        = trl->ld;
15432 			  tres->selected_entry = e;
15433 			  tres->info_used = trl->info_used;
15434 			  tres->serv      = trl->serv;
15435 			  newadr = address_from_ldap(tres);
15436 			  fs_give((void **)&tres);
15437 
15438 			  if(newadrs == NULL){
15439 			    newadrs = curnewadr = newadr;
15440 			  }
15441 			  else {
15442 			    curnewadr->next = newadr;
15443 			    curnewadr = newadr;
15444 			  }
15445 			  curi++;
15446 		      }
15447 		  }
15448 		  }
15449 		  if(newadrs == NULL || curnewadr == NULL){
15450 		      snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname");
15451 		      q_status_message(SM_ORDER, 0, 3, wtmp_20k_buf);
15452 		      newadr = copyaddr(curadr);
15453 		      if(newadrs == NULL){
15454 			  newadrs = curnewadr = newadr;
15455 		      }
15456 		      else {
15457 			  curnewadr->next = newadr;
15458 			  curnewadr = newadr;
15459 		      }
15460 		  }
15461 		  curnewadr->next = curadr ? curadr->next : NULL;
15462 		  if(curadr) curadr->next = NULL;
15463 		  if(curadr == adr)
15464 		    adr = newadrs;
15465 		  else{
15466 		    prevadr->next = newadrs;
15467 		    if(curadr)
15468 		      mail_free_address(&curadr);
15469 		  }
15470 	      }
15471 
15472 	      len = est_size(adr);
15473 	      ret_to = (char *)fs_get(len * sizeof(char));
15474 	      ret_to[0] = '\0';
15475 	      strip_personal_quotes(adr);
15476 	      rbuf.f   = dummy_soutr;
15477 	      rbuf.s   = NULL;
15478 	      rbuf.beg = ret_to;
15479 	      rbuf.cur = ret_to;
15480 	      rbuf.end = ret_to+len-1;
15481 	      rfc822_output_address_list(&rbuf, adr, 0L, NULL);
15482 	      *rbuf.cur = '\0';
15483 	      Tcl_SetResult(interp, ret_to, TCL_VOLATILE);
15484 	      fs_give((void **)&ret_to);
15485 	      fs_give((void **)&tmpaddrstr);
15486 	      fs_give((void **)&tmplistset);
15487 	      for(i = 0; lset[i]; i++)
15488 		fs_give((void **)&lset[i]);
15489 	      fs_give((void **)&lset);
15490 	      if(adr)
15491 		mail_free_address(&adr);
15492 
15493 	      return(TCL_OK);
15494 	  }
15495 	}
15496     }
15497 #endif /* ENABLE_LDAP */
15498     Tcl_SetResult(interp, err, TCL_STATIC);
15499     return(TCL_ERROR);
15500 }
15501 
15502 
15503 #ifdef ENABLE_LDAP
15504 int
peLdapQueryResults(Tcl_Interp * interp)15505 peLdapQueryResults(Tcl_Interp *interp)
15506 {
15507     WPLDAPRES_S     *tsl;
15508     Tcl_Obj         *secObj = NULL, *resObj = NULL, *itemObj;
15509     LDAPMessage     *e;
15510     LDAP_SERV_RES_S *trl;
15511     /* returned list will be of the form:
15512      *
15513      * {
15514      *  {search-string
15515      *   {name, {title, ...}, {unit, ...},
15516      *     {org, ...}, {email, ...}},
15517      *    ...
15518      *  },
15519      *  ...
15520      * }
15521      */
15522 
15523     for(tsl = wpldap_global->ldap_search_list;
15524 	tsl; tsl = tsl->next){
15525         secObj = Tcl_NewListObj(0, NULL);
15526 	if(Tcl_ListObjAppendElement(interp, secObj,
15527 		  Tcl_NewStringObj(tsl->str ? tsl->str
15528 				   : "", -1)) != TCL_OK)
15529 	    return(TCL_ERROR);
15530         resObj = Tcl_NewListObj(0, NULL);
15531 	for(trl = tsl->reslist; trl; trl = trl->next){
15532 	for(e = ldap_first_entry(trl->ld, trl->res);
15533 	    e != NULL;
15534 	    e = ldap_next_entry(trl->ld, e)){
15535 	    char       *dn;
15536 	    struct berval **cn, **org, **unit, **title, **mail, **sn;
15537 
15538 	    dn = NULL;
15539 	    cn = org = title = unit = mail = sn = NULL;
15540 
15541 	    itemObj = Tcl_NewListObj(0, NULL);
15542 	    peLdapEntryParse(trl, e, &cn, &org, &unit, &title,
15543 			     &mail, &sn);
15544 	    if(cn){
15545 	        if(Tcl_ListObjAppendElement(interp, itemObj,
15546 			Tcl_NewStringObj(cn[0]->bv_val, -1)) != TCL_OK)
15547 		    return(TCL_ERROR);
15548 		ldap_value_free_len(cn);
15549 	    }
15550 	    else if(sn){
15551 	        if(Tcl_ListObjAppendElement(interp, itemObj,
15552 		        Tcl_NewStringObj(sn[0]->bv_val, -1)) != TCL_OK)
15553 		    return(TCL_ERROR);
15554 		ldap_value_free_len(sn);
15555 	    }
15556 	    else{
15557 		dn = ldap_get_dn(trl->ld, e);
15558 
15559 		if(dn && !dn[0]){
15560 		    our_ldap_dn_memfree(dn);
15561 		    dn = NULL;
15562 		}
15563 
15564 	        if(Tcl_ListObjAppendElement(interp, itemObj,
15565 		        Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK)
15566 		    return(TCL_ERROR);
15567 
15568 		if(dn)
15569 		  our_ldap_dn_memfree(dn);
15570 	    }
15571 	    if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR)
15572 	      return(TCL_ERROR);
15573 	    if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR)
15574 	      return(TCL_ERROR);
15575 	    if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR)
15576 	      return(TCL_ERROR);
15577 	    if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR)
15578 	      return(TCL_ERROR);
15579 	    if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK)
15580 	      return(TCL_ERROR);
15581 	    if(title)
15582 	      ldap_value_free_len(title);
15583 	    if(unit)
15584 	      ldap_value_free_len(unit);
15585 	    if(org)
15586 	      ldap_value_free_len(org);
15587 	    if(mail)
15588 	      ldap_value_free_len(mail);
15589 	}
15590 	}
15591 	if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15592 	    return(TCL_ERROR);
15593 	if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15594 				    secObj) != TCL_OK)
15595 	    return(TCL_ERROR);
15596     }
15597     return(TCL_OK);
15598 }
15599 
15600 int
peLdapStrlist(Tcl_Interp * interp,Tcl_Obj * itemObj,struct berval ** strl)15601 peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, struct berval **strl)
15602 {
15603     Tcl_Obj *strlObj;
15604     int i;
15605 
15606     strlObj = Tcl_NewListObj(0, NULL);
15607     if(strl){
15608         for(i = 0; ALPINE_LDAP_usable(strl, i); i++){
15609 	    if(Tcl_ListObjAppendElement(interp, strlObj,
15610 		       Tcl_NewStringObj(strl[i]->bv_val, -1)) != TCL_OK)
15611 	        return(TCL_ERROR);
15612 	}
15613     }
15614     if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK)
15615       return(TCL_ERROR);
15616     return(TCL_OK);
15617 }
15618 
15619 
15620 int
init_ldap_pname(struct pine * ps)15621 init_ldap_pname(struct pine *ps)
15622 {
15623     if(!wps_global->VAR_PERSONAL_NAME
15624        || wps_global->VAR_PERSONAL_NAME[0] == '\0'){
15625         char *pname;
15626 	struct variable  *vtmp;
15627 
15628 	if(ps->maildomain && *ps->maildomain
15629 	   && ps->VAR_USER_ID && *ps->VAR_USER_ID){
15630 	    pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain);
15631 	    if(pname){
15632 	        vtmp = &ps->vars[V_PERSONAL_NAME];
15633 		if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0')
15634 		|| (vtmp->is_fixed && !vtmp->fixed_val.p)){
15635 		    if(vtmp->fixed_val.p)
15636 		      fs_give((void **)&vtmp->fixed_val.p);
15637 		    vtmp->fixed_val.p = cpystr(pname);
15638 		}
15639 		else {
15640 		  if(vtmp->global_val.p)
15641 		    fs_give((void **)&vtmp->global_val.p);
15642 		  vtmp->global_val.p = cpystr(pname);
15643 		}
15644 		fs_give((void **)&pname);
15645 		set_current_val(vtmp, FALSE, FALSE);
15646 	    }
15647 	}
15648     }
15649     return 0;
15650 }
15651 #endif /* ENABLE_LDAP */
15652 
15653 /*
15654  * Note: this is taken straight out of pico/composer.c
15655  *
15656  * strqchr - returns pointer to first non-quote-enclosed occurance of ch in
15657  *           the given string.  otherwise NULL.
15658  *      s -- the string
15659  *     ch -- the character we're looking for
15660  *      q -- q tells us if we start out inside quotes on entry and is set
15661  *           correctly on exit.
15662  *      m -- max characters we'll check for ch (set to -1 for no check)
15663  */
15664 char *
strqchr(char * s,int ch,int * q,int m)15665 strqchr(char *s, int ch, int *q, int m)
15666 {
15667     int	 quoted = (q) ? *q : 0;
15668 
15669     for(; s && *s && m != 0; s++, m--){
15670 	if(*s == '"'){
15671 	    quoted = !quoted;
15672 	    if(q)
15673 	      *q = quoted;
15674 	}
15675 
15676 	if(!quoted && *s == ch)
15677 	  return(s);
15678     }
15679 
15680     return(NULL);
15681 }
15682 
15683 
15684 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)15685 wp_prune_folders(CONTEXT_S  *ctxt,
15686 		 char	    *fcc,
15687 		 int	     cur_month,
15688 		 char	    *type,
15689 		 unsigned    pr,
15690 		 int	    *ok,
15691 		 int	     moved_fldrs,
15692 		 Tcl_Interp *interp)
15693 {
15694     Tcl_Obj *resObj = NULL, *secObj = NULL;
15695     char     path2[MAXPATH+1], tmp[21];
15696     int      exists, month_to_use;
15697     struct sm_folder *mail_list, *sm;
15698 
15699     mail_list = get_mail_list(ctxt, fcc);
15700 
15701     for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
15702       if(sm->month_num == cur_month - 1)
15703         break;  /* matched a month */
15704 
15705     month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
15706 
15707     if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){
15708 	strncpy(path2, fcc, sizeof(path2)-1);
15709 	path2[sizeof(path2)-1] = '\0';
15710 	strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1);
15711 	tmp[sizeof(tmp)-1] = '\0';
15712 	lcase((unsigned char *) tmp);
15713 	snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
15714 
15715 	if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){
15716 	    (*ok) = 0;
15717 	    return(NULL);
15718 	}
15719 	else if(exists & FEX_ISFILE){
15720 	    if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){
15721 	        prune_move_folder(fcc, path2, ctxt);
15722 	    } else {
15723 	        resObj = Tcl_NewListObj(0, NULL);
15724 		Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15725 		secObj = Tcl_NewListObj(0, NULL);
15726 		Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1));
15727 		Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1));
15728 		Tcl_ListObjAppendElement(interp, resObj, secObj);
15729 	    }
15730 	}
15731     }
15732     if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
15733        || pr == PRUNE_NO_AND_ASK){
15734         sm = mail_list;
15735         if(!resObj && sm && sm->name && sm->name[0] != '\0'){
15736 	    resObj = Tcl_NewListObj(0, NULL);
15737 	    Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15738 	    Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15739 	}
15740 	if(resObj)
15741 	    secObj = Tcl_NewListObj(0, NULL);
15742         for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){
15743 	    if(sm->name[0] == '\0')		/* can't happen */
15744 	      continue;
15745 	    Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1));
15746 	}
15747 	if(resObj)
15748 	  Tcl_ListObjAppendElement(interp, resObj, secObj);
15749     } else if(resObj)
15750       Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15751 
15752     free_folder_list(ctxt);
15753 
15754     if((sm = mail_list) != NULL){
15755 	while(sm->name){
15756 	    fs_give((void **)&(sm->name));
15757 	    sm++;
15758 	}
15759 
15760         fs_give((void **)&mail_list);
15761     }
15762 
15763     return(resObj);
15764 }
15765 
15766 
15767 int
hex_colorstr(char * hexcolor,char * str)15768 hex_colorstr(char *hexcolor, char *str)
15769 {
15770     char *tstr, *p, *p2, tbuf[256];
15771     int i;
15772 
15773     strcpy(hexcolor, "000000");
15774     tstr = color_to_asciirgb(str);
15775     p = tstr;
15776     p2 = strindex(p, ',');
15777     if(p2 == NULL) return 0;
15778     strncpy(tbuf, p, min(50, p2-p));
15779     i = atoi(tbuf);
15780     sprintf(hexcolor, "%2.2x", i);
15781     p = p2+1;
15782     p2 = strindex(p, ',');
15783     if(p2 == NULL) return 0;
15784     strncpy(tbuf, p, min(50, p2-p));
15785     i = atoi(tbuf);
15786     sprintf(hexcolor+2, "%2.2x", i);
15787     p = p2+1;
15788     strncpy(tbuf, p, 50);
15789     i = atoi(tbuf);
15790     sprintf(hexcolor+4, "%2.2x", i);
15791 
15792     return 0;
15793 }
15794 
15795 int
hexval(char ch)15796 hexval(char ch)
15797 {
15798     if(ch >= '0' && ch <= '9')
15799       return (ch - '0');
15800     else if (ch >= 'A' && ch <= 'F')
15801       return (10 + (ch - 'A'));
15802     else if (ch >= 'a' && ch <= 'f')
15803       return (10 + (ch - 'a'));
15804     return -1;
15805 }
15806 
15807 int
ascii_colorstr(char * acolor,char * hexcolor)15808 ascii_colorstr(char *acolor, char *hexcolor)
15809 {
15810     int i, hv;
15811 
15812     if(strlen(hexcolor) > 6) return 1;
15813     /* red value */
15814     if((hv = hexval(hexcolor[0])) == -1) return 1;
15815     i = 16 * hv;
15816     if((hv = hexval(hexcolor[1])) == -1) return 1;
15817     i += hv;
15818     sprintf(acolor, "%3.3d,", i);
15819     /* green value */
15820     if((hv = hexval(hexcolor[2])) == -1) return 1;
15821     i = 16 * hv;
15822     if((hv = hexval(hexcolor[3])) == -1) return 1;
15823     i += hv;
15824     sprintf(acolor+4, "%3.3d,", i);
15825     /* blue value */
15826     if((hv = hexval(hexcolor[4])) == -1) return 1;
15827     i = 16 * hv;
15828     if((hv = hexval(hexcolor[5])) == -1) return 1;
15829     i += hv;
15830     sprintf(acolor+8, "%3.3d", i);
15831 
15832     return 0;
15833 }
15834 
15835 
15836 char *
peRandomString(char * b,int l,int f)15837 peRandomString(char *b, int l, int f)
15838 {
15839     static char          *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
15840     char		 *s = b;
15841     int			  j;
15842     long		  n;
15843 
15844     while(1){
15845 	n = random();
15846 	for(j = 0; j < ((sizeof(long) * 8) / 5); j++){
15847 	    if(l-- <= 0){
15848 		*s = '\0';
15849 		return(b);
15850 	    }
15851 
15852 	    switch(f){
15853 	      case PRS_LOWER_CASE :
15854 		*s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15855 		break;
15856 
15857 	      case PRS_MIXED_CASE :
15858 		if(random() % 2){
15859 		    *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15860 		    break;
15861 		}
15862 
15863 	      default :
15864 		*s++ = kb[(n & 0x1F)];
15865 		break;
15866 	    }
15867 
15868 	    n = n >> 5;
15869 	}
15870     }
15871 }
15872 
15873 
15874 long
peAppendMsg(MAILSTREAM * stream,void * data,char ** flags,char ** date,STRING ** message)15875 peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message)
15876 {
15877   char *t,*t1,tmp[MAILTMPLEN];
15878   unsigned long u;
15879   MESSAGECACHE *elt;
15880   APPEND_PKG *ap = (APPEND_PKG *) data;
15881   *flags = *date = NIL;		/* assume no flags or date */
15882   if (ap->flags) fs_give ((void **) &ap->flags);
15883   if (ap->date) fs_give ((void **) &ap->date);
15884   mail_gc (ap->stream,GC_TEXTS);
15885   if (++ap->msgno <= ap->msgmax) {
15886 				/* initialize flag string */
15887     memset (t = tmp,0,MAILTMPLEN);
15888 				/* output system flags */
15889     if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15890     if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15891     if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15892     if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15893     if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15894     if ((u = elt->user_flags) != 0L) do	/* any user flags? */
15895       if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long)
15896 	  (2 + strlen
15897 	   (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) {
15898 	if(t-tmp < sizeof(tmp))
15899 	  *t++ = ' ';		/* space delimiter */
15900 	strncpy (t,t1,sizeof(tmp)-(t-tmp));	/* copy the user flag */
15901       }
15902     while (u);			/* until no more user flags */
15903     tmp[sizeof(tmp)-1] = '\0';
15904     *flags = ap->flags = cpystr (tmp + 1);
15905     *date = ap->date = cpystr (mail_date (tmp,elt));
15906     *message = ap->message;	/* message stringstruct */
15907     INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
15908   }
15909   else *message = NIL;		/* all done */
15910   return LONGT;
15911 }
15912 
15913 
15914 /* Initialize file string structure for file stringstruct
15915 * Accepts: string structure
15916  *	    pointer to message data structure
15917  *	    size of string
15918  */
15919 
15920 void
ms_init(STRING * s,void * data,unsigned long size)15921 ms_init(STRING *s, void *data, unsigned long size)
15922 {
15923   APPEND_PKG *md = (APPEND_PKG *) data;
15924   s->data = data;		/* note stream/msgno and header length */
15925   mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT);
15926   mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL);
15927   s->size += s->data1;		/* header + body size */
15928   SETPOS (s,0);
15929 }
15930 
15931 
15932 /* Get next character from file stringstruct
15933  * Accepts: string structure
15934  * Returns: character, string structure chunk refreshed
15935  */
15936 char
ms_next(STRING * s)15937 ms_next(STRING *s)
15938 {
15939   char c = *s->curpos++;	/* get next byte */
15940   SETPOS (s,GETPOS (s));	/* move to next chunk */
15941   return c;			/* return the byte */
15942 }
15943 
15944 
15945 /* Set string pointer position for file stringstruct
15946  * Accepts: string structure
15947  *	    new position
15948  */
15949 void
ms_setpos(STRING * s,unsigned long i)15950 ms_setpos(STRING *s, unsigned long i)
15951 {
15952   APPEND_PKG *md = (APPEND_PKG *) s->data;
15953   if (i < s->data1) {		/* want header? */
15954     s->chunk = mail_fetchheader (md->stream,md->msgno);
15955     s->chunksize = s->data1;	/* header length */
15956     s->offset = 0;		/* offset is start of message */
15957   }
15958   else if (i < s->size) {	/* want body */
15959     s->chunk = mail_fetchtext (md->stream,md->msgno);
15960     s->chunksize = s->size - s->data1;
15961     s->offset = s->data1;	/* offset is end of header */
15962   }
15963   else {			/* off end of message */
15964     s->chunk = NIL;		/* make sure that we crack on this then */
15965     s->chunksize = 1;		/* make sure SNX cracks the right way... */
15966     s->offset = i;
15967   }
15968 				/* initial position and size */
15969   s->curpos = s->chunk + (i -= s->offset);
15970   s->cursize = s->chunksize - i;
15971 }
15972 
15973 
15974 int
remote_pinerc_failure(void)15975 remote_pinerc_failure(void)
15976 {
15977     snprintf(wps_global->last_error, sizeof(wps_global->last_error), "%s",
15978 	     wps_global->c_client_error[0]
15979 	       ? wps_global->c_client_error
15980 	       : _("Unable to read remote configuration"));
15981 
15982     return(TRUE);
15983 }
15984 
15985 char *
peWebAlpinePrefix(void)15986 peWebAlpinePrefix(void)
15987 {
15988     return("Web ");
15989 }
15990 
15991 
peNewMailAnnounce(MAILSTREAM * stream,long n,long t_nm_count)15992 void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){
15993     char      subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
15994 	     *folder = NULL, intro[MAILTMPLEN+1];
15995     long      number;
15996     ENVELOPE *e = NULL;
15997     Tcl_Obj  *resObj;
15998 
15999     if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){
16000 
16001 	Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream)));
16002 	Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n)));
16003 
16004 	if(stream){
16005 	    e = pine_mail_fetchstructure(stream, n, NULL);
16006 
16007 	    if(sp_flagged(stream, SP_INBOX))
16008 	      folder = NULL;
16009 	    else{
16010 		folder = STREAMNAME(stream);
16011 		if(folder[0] == '?' && folder[1] == '\0')
16012 		  folder = NULL;
16013 	    }
16014 	}
16015 
16016 	format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro));
16017 
16018 	snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
16019 		 "%s%s%s%.80s%.80s", intro,
16020 		 from ? ((number > 1L) ? " Most recent f" : " F") : "",
16021 		 from ? "rom " : "",
16022 		 from ? from : "",
16023 		 subjtext);
16024 
16025 	Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(wtmp_20k_buf,-1));
16026 
16027 	Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj);
16028     }
16029 }
16030 
16031 
16032 /* * * * * * * * * RSS 2.0 Support Routines  * * * * * * * * * * * */
16033 
16034 /*
16035  * PERssCmd - RSS TCL interface
16036  */
16037 int
PERssCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])16038 PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
16039 {
16040     char *s1;
16041 
16042     dprint((2, "PERssCmd"));
16043 
16044     if(objc == 1){
16045 	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
16046 	return(TCL_ERROR);
16047     }
16048     s1 = Tcl_GetStringFromObj(objv[1], NULL);
16049 
16050     if(s1){
16051 	if(!strcmp(s1, "news")){
16052 	    return(peRssReturnFeed(interp, "news", wps_global->VAR_RSS_NEWS));
16053 	}
16054 	else if(!strcmp(s1, "weather")){
16055 	    return(peRssReturnFeed(interp, "weather", wps_global->VAR_RSS_WEATHER));
16056 	}
16057     }
16058 
16059     Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC);
16060     return(TCL_ERROR);
16061 }
16062 
16063 /*
16064  * peRssReturnFeed - fetch feed contents and package Tcl response
16065  */
16066 int
peRssReturnFeed(Tcl_Interp * interp,char * type,char * link)16067 peRssReturnFeed(Tcl_Interp *interp, char *type, char *link)
16068 {
16069     RSS_FEED_S *feed;
16070     char       *errstr = "UNKNOWN";
16071 
16072     if(link){
16073 	wps_global->c_client_error[0] = '\0';
16074 
16075 	if((feed = peRssFeed(interp, type, link)) != NULL)
16076 	  return(peRssPackageFeed(interp, feed));
16077 
16078 	if(wps_global->mm_log_error)
16079 	  errstr = wps_global->c_client_error;
16080     }
16081     else
16082       errstr = "missing setting";
16083 
16084     snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr);
16085     Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
16086     return(TCL_ERROR);
16087 }
16088 
16089 /*
16090  * peRssPackageFeed - build a list of feed item elements
16091  *
16092  *	LIST ORDER: {title} {link} {description} {image}
16093  */
16094 int
peRssPackageFeed(Tcl_Interp * interp,RSS_FEED_S * feed)16095 peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed)
16096 {
16097     RSS_ITEM_S *item;
16098 
16099     for(item = feed->items; item; item = item->next)
16100       if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s",
16101 		    (item->title && *item->title)? item->title : "Feed Provided No Title",
16102 		    item->link ? item->link : "",
16103 		    item->description ? item->description : "",
16104 		    feed->image ? feed->image : "") != TCL_OK)
16105 	return(TCL_ERROR);
16106 
16107     return(TCL_OK);
16108 }
16109 
16110 
16111 /*
16112  * peRssFeed - return cached feed struct or fetch a new one
16113  */
16114 RSS_FEED_S *
peRssFeed(Tcl_Interp * interp,char * type,char * link)16115 peRssFeed(Tcl_Interp *interp, char *type, char *link)
16116 {
16117     int		 i, cache_l, cp_ref;
16118     time_t	 now = time(0);
16119     RSS_FEED_S	*feed = NULL;
16120     RSS_CACHE_S	*cache, *cp;
16121     static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE];
16122 
16123     if(!strucmp(type,"news")){
16124 	cache   =  &news_cache[0];
16125 	cache_l = RSS_NEWS_CACHE_SIZE;
16126     }
16127     else{
16128 	cache   = &weather_cache[0];
16129 	cache_l = RSS_WEATHER_CACHE_SIZE;
16130     }
16131 
16132     /* search/purge cache */
16133     for(i = 0; i < cache_l; i++)
16134       if(cache[i].link){
16135 	  if(now > cache[i].stale){
16136 	      peRssClearCacheEntry(&cache[i]);
16137 	  }
16138 	  else if(!strcmp(link, cache[i].link)){
16139 	      cache[i].referenced++;
16140 	      return(cache[i].feed); /* HIT! */
16141 	  }
16142       }
16143 
16144     if((feed = peRssFetch(interp, link)) != NULL){
16145 	/* find cache slot, and insert feed into cache */
16146 	for(i = 0, cp_ref = 0; i < cache_l; i++)
16147 	  if(!cache[i].feed){
16148 	      cp = &cache[i];
16149 	      break;
16150 	  }
16151 	  else if(cache[i].referenced >= cp_ref)
16152 	    cp = &cache[i];
16153 
16154 	if(!cp)
16155 	  cp = &cache[0];		/* failsafe */
16156 
16157 	peRssClearCacheEntry(cp);	/* make sure */
16158 
16159 	cp->link       = cpystr(link);
16160 	cp->feed       = feed;
16161 	cp->referenced = 0;
16162 	cp->stale      = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60);
16163     }
16164 
16165     return(feed);
16166 }
16167 
16168 /*
16169  * peRssFetch - follow the provided link an return the resulting struct
16170  */
16171 RSS_FEED_S *
peRssFetch(Tcl_Interp * interp,char * link)16172 peRssFetch(Tcl_Interp *interp, char *link)
16173 {
16174     char	  *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL;
16175     char	  *buffer = NULL, *bp, *p, *q;
16176     int		   ttl = 60;
16177     unsigned long  port = 0L, buffer_len = 0L;
16178     time_t	   theirdate = 0;
16179     STORE_S	  *feed_so = NULL;
16180     TCPSTREAM	  *tcp_stream;
16181 
16182     if(link){
16183 	/* grok url */
16184 	rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag);
16185 	if(scheme && loc && path){
16186 	    if((p = strchr(loc,':')) != NULL){
16187 		*p++  = '\0';
16188 		while(*p && isdigit((unsigned char) *p))
16189 		  port = ((port * 10) + (*p++ - '0'));
16190 
16191 		if(*p){
16192 		    Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC);
16193 		    peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16194 		    return(NULL);
16195 		}
16196 	    }
16197 
16198 	    if(scheme && !strucmp(scheme, "feed")){
16199 		fs_give((void **) &scheme);
16200 		scheme = cpystr("http");
16201 	    }
16202 
16203 	    mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5);
16204 	    tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT);
16205 	    mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
16206 
16207 	    if(tcp_stream != NULL){
16208 		char rev[128];
16209 
16210 		snprintf(wtmp_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",
16211 			 path, parms ? ":" : "", parms ? parms : "",
16212 			 query ? "?" : "", query ? query : "", loc,
16213 			 ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev)));
16214 
16215 		mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5);
16216 		mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5);
16217 
16218 		if(tcp_sout(tcp_stream, wtmp_20k_buf, strlen(wtmp_20k_buf))){
16219 		    int ok = 0, chunked = FALSE;
16220 
16221 		    while((p = tcp_getline(tcp_stream)) != NULL){
16222 			if(!ok){
16223 			    ok++;
16224 			    if(strucmp(p,"HTTP/1.1 200 OK")){
16225 				fs_give((void **) &p);
16226 				break;			/* bail */
16227 			    }
16228 			}
16229 			else if(*p == '\0'){		/* first blank line, start of body  */
16230 			    if(buffer || feed_so){
16231 				fs_give((void **) &p);
16232 				break;			/* bail */
16233 			    }
16234 
16235 			    if(buffer_len){
16236 				buffer = fs_get(buffer_len + 16);
16237 				if(!tcp_getbuffer(tcp_stream, buffer_len, buffer))
16238 				  fs_give((void **) &buffer);
16239 
16240 				fs_give((void **) &p);
16241 				break;			/* bail */
16242 			    }
16243 			    else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
16244 				fs_give((void **) &p);
16245 				break;			/* bail */
16246 			    }
16247 			}
16248 			else if(feed_so){		/* collect body */
16249 			    if(chunked){
16250 				int chunk_len = 0, gotbuf;
16251 
16252 				/* first line is chunk size in hex */
16253 				for(q = p; *q && isxdigit((unsigned char) *q); q++)
16254 				  chunk_len = (chunk_len * 16) + XDIGIT2C(*q);
16255 
16256 				if(chunk_len > 0){	/* collect chunk */
16257 				    char *tbuf = fs_get(chunk_len + 16);
16258 				    gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf);
16259 				    if(gotbuf)
16260 				      so_nputs(feed_so, tbuf, chunk_len);
16261 
16262 				    fs_give((void **) &tbuf);
16263 
16264 				    if(!gotbuf){
16265 					fs_give((void **) &p);
16266 					break;		/* bail */
16267 				    }
16268 				}
16269 
16270 				/* collect trailing CRLF */
16271 				gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0');
16272 				if(q)
16273 				  fs_give((void **) &q);
16274 
16275 				if(chunk_len == 0 || !gotbuf){
16276 				    fs_give((void **) &p);
16277 				    break;		/* bail */
16278 				}
16279 			    }
16280 			    else
16281 			      so_puts(feed_so, p);
16282 			}
16283 			else{				/* in header, grok fields */
16284 			    if((q = strchr(p,':')) != NULL){
16285 				int l = q - p;
16286 
16287 				*q++ = '\0';
16288 				while(isspace((unsigned char ) *q))
16289 				  q++;
16290 
16291 				/* content-length */
16292 				if(l == 4 && !strucmp(p, "date")){
16293 				    theirdate = date_to_local_time_t(q);
16294 				}
16295 				else if(l == 7 && !strucmp(p, "expires")){
16296 				    time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0));
16297 
16298 				    if(expires > 0 && expires < (8 * 60 * 60))
16299 				      ttl = expires;
16300 				}
16301 				else if(l == 12 && !strucmp(p, "content-type")
16302 					&& struncmp(q,"text/xml", 8)
16303 					&& struncmp(q,"application/xhtml+xml", 21)
16304 					&& struncmp(q,"application/rss+xml", 19)
16305 					&& struncmp(q,"application/xml", 15)){
16306 				    fs_give((void **) &p);
16307 				    break;		/* bail */
16308 				}
16309 				else if(l == 13 && !strucmp(p, "cache-control")){
16310 				    if(!struncmp(q,"max-age=",8)){
16311 					int secs = 0;
16312 
16313 					for(q += 8; *q && isdigit((unsigned char) *q); q++)
16314 					  secs = ((secs * 10) + (*q - '0'));
16315 
16316 					if(secs > 0)
16317 					  ttl = secs;
16318 				    }
16319 				}
16320 				else if(l == 14 && !strucmp(p,"content-length")){
16321 				    while(*q && isdigit((unsigned char) *q))
16322 				      buffer_len = ((buffer_len * 10) + (*q++ - '0'));
16323 
16324 				    if(*q){
16325 					fs_give((void **) &p);
16326 					break;		/* bail */
16327 				    }
16328 				}
16329 				else if(l == 17 && !strucmp(p, "transfer-encoding")){
16330 				    if(!struncmp(q,"chunked", 7)){
16331 					chunked = TRUE;
16332 				    }
16333 				    else{		/* unknown encoding */
16334 					fs_give((void **) &p);
16335 					break;		/* bail */
16336 				    }
16337 				}
16338 			    }
16339 			}
16340 
16341 			fs_give((void **) &p);
16342 		    }
16343 		}
16344 		else{
16345 		    Tcl_SetResult(interp, "RSS send failure", TCL_STATIC);
16346 		    peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16347 		}
16348 
16349 		tcp_close(tcp_stream);
16350 		mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
16351 		mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60);
16352 		peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16353 
16354 		if(feed_so){
16355 		    buffer = (char *) so_text(feed_so);
16356 		    buffer_len = (int) so_tell(feed_so);
16357 		}
16358 
16359 		if(buffer && buffer_len){
16360 		    RSS_FEED_S *feed;
16361 		    char       *err;
16362 		    STORE_S    *bucket;
16363 		    gf_io_t	gc, pc;
16364 
16365 		    /* grok response */
16366 		    bucket = so_get(CharStar, NULL, EDIT_ACCESS);
16367 		    gf_set_readc(&gc, buffer, buffer_len, CharStar, 0);
16368 		    gf_set_so_writec(&pc, bucket);
16369 		    gf_filter_init();
16370 		    gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0));
16371 		    if((err = gf_pipe(gc, pc)) != NULL){
16372 			gf_html2plain_rss_free(&feed);
16373 			Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16374 		    }
16375 
16376 		    so_give(&bucket);
16377 
16378 		    if(feed_so)
16379 		      so_give(&feed_so);
16380 		    else
16381 		      fs_give((void **) &buffer);
16382 
16383 		    return(feed);
16384 		}
16385 		else
16386 		  Tcl_SetResult(interp, "RSS response error", TCL_STATIC);
16387 	    }
16388 	    else
16389 	      Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16390 	}
16391 	else
16392 	  Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC);
16393     }
16394     else
16395       Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC);
16396 
16397     return(NULL);
16398 }
16399 
16400 
16401 void
peRssComponentFree(char ** scheme,char ** loc,char ** path,char ** parms,char ** query,char ** frag)16402 peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag)
16403 {
16404     if(scheme) fs_give((void **) scheme);
16405     if(loc) fs_give((void **) loc);
16406     if(path) fs_give((void **) path);
16407     if(parms) fs_give((void **) parms);
16408     if(query) fs_give((void **) query);
16409     if(frag) fs_give((void **) frag);
16410 }
16411 
16412 void
peRssClearCacheEntry(RSS_CACHE_S * entry)16413 peRssClearCacheEntry(RSS_CACHE_S *entry)
16414 {
16415     if(entry){
16416 	if(entry->link)
16417 	  fs_give((void **) &entry->link);
16418 
16419 	gf_html2plain_rss_free(&entry->feed);
16420 	memset(entry, 0, sizeof(RSS_CACHE_S));
16421     }
16422 }
16423