* Clear even more warnings from clang 3.5 and MAC OSX.
[alpine.git] / web / src / alpined.d / alpined.c
blobc2e43fcb227ed73758252f842becdfcbf6b4d3b7
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
5 /* ========================================================================
6 * Copyright 2006-2008 University of Washington
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
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * ========================================================================
17 /* ========================================================================
18 Implement alpine TCL interfaces. Execute TCL interfaces
19 via interpreter reading commands and writing results over
20 UNIX domain socket.
21 ======================================================================== */
24 #include <system.h>
25 #include <general.h>
27 #include "../../../c-client/c-client.h"
28 #include "../../../c-client/imap4r1.h"
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"
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"
86 #include "alpined.h"
87 #include "color.h"
88 #include "imap.h"
89 #include "ldap.h"
90 #include "debug.h"
91 #include "stubs.h"
93 #include <tcl.h>
97 * Fake screen dimension for word wrap and such
99 #define FAKE_SCREEN_WIDTH 80
100 #define FAKE_SCREEN_LENGTH 24
103 * Aribtrary minimum display width (in characters)
105 #define MIN_SCREEN_COLS 20
109 * Maximum number of lines allowed in signatures
111 #define SIG_MAX_LINES 24
112 #define SIG_MAX_COLS 1024
116 * Number of seconds we'll wait before we assume the client has wondered
117 * on to more interesting content
119 #define PE_INPUT_TIMEOUT 1800
123 * Posting error lenght max
125 #define WP_MAX_POST_ERROR 128
129 * AUTH Response Tokens
131 #define AUTH_EMPTY_STRING "NOPASSWD"
132 #define AUTH_FAILURE_STRING "BADPASSWD"
135 * CERT Response Tokens
137 #define CERT_QUERY_STRING "CERTQUERY"
138 #define CERT_FAILURE_STRING "CERTFAIL"
142 * Charset used within alpined and to communicate with alpined
143 * Note: posting-charset still respected
145 #define WP_INTERNAL_CHARSET "UTF-8"
149 * Globals referenced throughout pine...
151 struct pine *ps_global; /* THE global variable! */
155 * More global state
157 long gPeITop, gPeICount;
159 long gPeInputTimeout = PE_INPUT_TIMEOUT;
160 long gPEAbandonTimeout = 0;
164 * Authorization issues
166 int peNoPassword, peCredentialError;
167 int peCertFailure, peCertQuery;
168 char peCredentialRequestor[CRED_REQ_SIZE];
170 char *peSocketName;
172 char **peTSig;
174 CONTEXT_S *config_context_list;
176 STRLIST_S *peCertHosts;
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)))
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;
196 static STATMSG_S *peStatList;
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;
222 static COMPATT_S *peCompAttach;
225 * Holds data passed
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;
246 * locally global structure to keep track of various bits of state
247 * needed to collect filtered output
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;
268 * RSS stream cache
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;
277 #define RSS_NEWS_CACHE_SIZE 1
278 #define RSS_WEATHER_CACHE_SIZE 1
281 #ifdef ENABLE_LDAP
282 WPLDAP_S *wpldap_global;
283 #endif
286 * random string generator flags
288 #define PRS_NONE 0x0000
289 #define PRS_LOWER_CASE 0x0001
290 #define PRS_UPPER_CASE 0x0002
291 #define PRS_MIXED_CASE 0x0004
294 * peSaveWork flag definitions
296 #define PSW_NONE 0x00
297 #define PSW_COPY 0x01
298 #define PSW_MOVE 0x02
301 * Message Collector flags
303 #define PMC_NONE 0x00
304 #define PMC_FORCE_QUAL 0x01
305 #define PMC_PRSRV_ATT 0x02
308 * length of thread info string
310 #define WP_MAX_THRD_S 64
313 * static buf size for putenv() if necessary
315 #define PUTENV_MAX 64
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...
330 ----*/
331 char tmp_20k_buf[20480];
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 *);
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[]);
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;
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 */
535 /*----------------------------------------------------------------------
536 main routine -- entry point
538 Args: argv, argc -- The command line arguments
541 Setup c-client drivers and dive into TCL interpreter engine
543 ----*/
546 main(int argc, char *argv[])
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
556 srandom(getpid() + time(0));
558 /*----------------------------------------------------------------------
559 Initialize c-client
560 ----------------------------------------------------------------------*/
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
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 */
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);
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
588 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){
589 fprintf(stderr, "Can't disable unix driver");
590 exit(1);
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.
601 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
602 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
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;
611 setup_for_index_index_screen();
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;
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);
631 /*----------------------------------------------------------------------
632 Hop into the Tcl processing loop
633 ----------------------------------------------------------------------*/
635 buf = (char *) fs_get(bl * sizeof(char));
637 if(fgets(sname, 255, stdin) && *sname){
638 if(sname[l = strlen(sname) - 1] == '\n')
639 sname[l] = '\0';
641 if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){
643 name.sun_family = AF_UNIX;
644 strcpy(name.sun_path, peSocketName = sname);
645 l = sizeof(name);
647 if(bind(s, (struct sockaddr *) &name, l) == 0){
648 if(listen(s, 5) == 0){
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.
655 if(debug < 10){
656 switch(fork()){
657 case -1 : /* error */
658 perror("fork");
659 exit(1);
661 case 0 : /* child */
662 close(0); /* disassociate */
663 close(1);
664 close(2);
665 setpgrp(0, 0);
666 break;
668 default : /* parent */
669 exit(0);
673 debug_init();
674 dprint((SYSDBG_INFO, "started"));
676 interp = Tcl_CreateInterp();
678 PEInit(interp, sname);
680 while(1){
681 struct timeval tv;
682 fd_set rfd;
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;
691 gPEAbandonTimeout = 0;
693 if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){
694 dprint((SYSDBG_ERR, "accept failure: %s",
695 error_description(errno)));
696 break;
699 dprint((5, "accept success: %d", cs));
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.
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;
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));
722 break;
724 else
725 x = (x * 10) + (buf[i] - '0');
728 if(o && o == l)
729 break;
732 if(n == 0){
733 dprint((SYSDBG_ERR, "read EOF"));
735 else if(n < 0){
736 dprint((SYSDBG_ERR, "read failure: %s", error_description(errno)));
738 else{
739 buf[o] = '\0';
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;
746 snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]);
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;
755 for(p = &dbuf[25]; *p; p++)
756 *p = 'X';
759 dprint((1, dbuf));
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;
771 close(cs);
773 else if(errno != EINTR){
774 if(n < 0){
775 dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno)));
777 else{
778 dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec));
781 Tcl_Exit(0);
783 /* Tcl_Exit should never return. Getting here is an error. */
784 dprint((SYSDBG_ERR, "Tcl_Exit failure"));
788 else
789 perror("listen");
791 else
792 perror("bind");
794 close(s);
795 unlink(sname);
797 else
798 perror("socket");
800 else
801 fprintf(stderr, "Can't read socket name\n");
803 exit(ev);
808 * peReturn - common routine to return TCL result
810 void
811 peReturn(int sock, char *status, const char *result)
813 if(peWrite(sock, status))
814 if(peWrite(sock, "\n"))
815 peWrite(sock, (char *) result);
819 * peWrite - write all the given string on the given socket
822 peWrite(int sock, char *s)
824 int i, n;
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);
832 return(1);
836 * PEInit - Initialize exported TCL functions
839 PEInit(Tcl_Interp *interp, char *sname)
841 dprint((2, "PEInit: %s", sname));
843 if(Tcl_Init(interp) == TCL_ERROR) {
844 return(TCL_ERROR);
847 Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd,
848 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
850 Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd,
851 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
853 Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd,
854 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
856 Tcl_CreateObjCommand(interp, "PESession", PESessionCmd,
857 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
859 Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd,
860 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
862 Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd,
863 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
865 Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd,
866 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
868 Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd,
869 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
871 Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd,
872 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
874 Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd,
875 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
877 Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd,
878 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
880 Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd,
881 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
883 Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd,
884 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
886 Tcl_CreateObjCommand(interp, "PERss", PERssCmd,
887 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
889 Tcl_CreateExitHandler(PEExitCleanup, sname);
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 */
897 return(TCL_OK);
901 void
902 PEExitCleanup(ClientData clientData)
904 dprint((4, "PEExitCleanup"));
906 if(ps_global){
907 /* destroy any open stream */
908 peDestroyStream(ps_global);
910 /* destroy user context */
911 peDestroyUserContext(&ps_global);
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);
920 #endif /* ENABLE_LDAP */
922 if((char *) clientData)
923 unlink((char *) clientData);
925 peFreeAttach(&peCompAttach);
927 dprint((SYSDBG_INFO, "finished"));
932 * PEInfoCmd - export various bits of alpine state
935 PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
937 char *err = "Unknown PEInfo request";
939 dprint((2, "PEInfoCmd"));
941 if(objc == 1){
942 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
944 else{
945 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
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;
956 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
957 Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
958 return(TCL_ERROR);
961 if(!strcmp(varname, "viewer-hdr-colors")){
962 char *newhdr = NULL, *newpat = NULL, *utype;
963 int hindex, i;
965 if(objc < 5){
966 Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
967 return(TCL_ERROR);
970 hcolors = spec_colors_from_varlist(ps_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);
976 if(!strcmp(utype, "delete")){
977 if(!hcolors){
978 Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
979 return(TCL_ERROR);
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);
987 if(hindex == 0){
988 thc = hcolors;
989 hcolors = hcolors->next;
990 thc->next = NULL;
991 free_spec_colors(&thc);
993 else{
994 /* zero based */
995 for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
998 if(thc && thc->next){
999 SPEC_COLOR_S *thc2 = thc->next;
1001 thc->next = thc2->next;
1002 thc2->next = NULL;
1003 free_spec_colors(&thc2);
1005 else{
1006 Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
1007 return(TCL_ERROR);
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);
1017 if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
1018 return (TCL_ERROR);
1020 if(cObjc != 2){
1021 Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
1022 return(TCL_ERROR);
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);
1030 if(cObjc != 2){
1031 Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
1032 return(TCL_ERROR);
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;
1040 for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
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");
1049 if(newpat && *newpat)
1050 (*hcp)->val = string_to_pattern(newpat);
1051 else
1052 (*hcp)->val = NULL;
1054 (*hcp)->next = NULL;
1056 else{
1057 Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
1058 return(TCL_ERROR);
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);
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);
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);
1084 for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
1087 if(!thc){
1088 Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
1089 return (TCL_ERROR);
1092 if(thc->spec)
1093 fs_give((void **)&thc->spec);
1095 thc->spec = cpystr(newhdr);
1096 if(ascii_colorstr(asciicolor, fghex) == 0) {
1097 if(thc->fg)
1098 fs_give((void **)&thc->fg);
1100 thc->fg = cpystr(asciicolor);
1102 else{
1103 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
1104 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1105 return(TCL_ERROR);
1108 if(ascii_colorstr(asciicolor, bghex) == 0) {
1109 if(thc->bg)
1110 fs_give((void **)&thc->bg);
1112 thc->bg = cpystr(asciicolor);
1114 else{
1115 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1116 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1117 return(TCL_ERROR);
1120 if(thc->val)
1121 fs_give((void **)&thc->val);
1123 if(newpat && *newpat){
1124 thc->val = string_to_pattern(newpat);
1127 else{
1128 Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
1129 return(TCL_ERROR);
1132 vtmp = &ps_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]);
1136 if(vtmp->main_user_val.l)
1137 fs_give((void **)&vtmp->main_user_val.l);
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);
1144 else {
1145 if(objc != 4){
1146 Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
1147 return(TCL_ERROR);
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);
1158 snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
1159 for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
1160 vtmp->name && strucmp(vtmp->name, tvname);
1161 vtmp++)
1164 if(!vtmp->name || vtmp->is_list){
1165 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1166 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1167 return(TCL_ERROR);
1170 if(ascii_colorstr(asciicolor, fghex) == 0) {
1171 if(vtmp->main_user_val.p)
1172 fs_give((void **)&vtmp->main_user_val.p);
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);
1179 else{
1180 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
1181 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1182 return(TCL_ERROR);
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 = &ps_global->vars[V_NORM_FORE_COLOR];
1189 vtmp->name && strucmp(vtmp->name, tvname);
1190 vtmp++)
1193 if(!vtmp->name || vtmp->is_list){
1194 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1195 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1196 return(TCL_ERROR);
1199 if(ascii_colorstr(asciicolor, bghex) == 0) {
1200 if(vtmp->main_user_val.p)
1201 fs_give((void **)&vtmp->main_user_val.p);
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);
1208 else{
1209 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1210 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
1211 return(TCL_ERROR);
1214 Tcl_SetResult(interp, "1", TCL_STATIC);
1215 return(TCL_OK);
1218 else if(!strcmp(s1, "lappend")){
1219 if(objc >= 4){
1220 Tcl_Obj *dObj;
1221 int i;
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);
1228 if(i == objc){
1229 return(TCL_OK);
1232 else
1233 err = "PEInfo lappend: Unknown list name";
1235 else
1236 err = "PEInfo lappend: Too few args";
1238 else if(objc == 2){
1239 if(!strcmp(s1, "version")){
1240 char buf[256];
1243 * CMD: version
1245 * Returns: string representing Pine version
1246 * engine built on
1248 Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC);
1249 return(TCL_OK);
1251 else if(!strcmp(s1, "revision")){
1252 char buf[16];
1255 * CMD: revision
1257 * Returns: string representing Pine SVN revision
1258 * engine built on
1261 Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE);
1262 return(TCL_OK);
1264 else if(!strcmp(s1, "key")){
1265 static char key[64];
1267 if(!key[0])
1268 peRandomString(key,32,PRS_UPPER_CASE);
1270 Tcl_SetResult(interp, key, TCL_STATIC);
1271 return(TCL_OK);
1273 else if(!strcmp(s1, "indexheight")){
1274 Tcl_SetResult(interp, ps_global->VAR_WP_INDEXHEIGHT ?
1275 ps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE);
1276 return(TCL_OK);
1278 else if(!strcmp(s1, "indexlines")){
1279 Tcl_SetResult(interp, ps_global->VAR_WP_INDEXLINES ?
1280 ps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE);
1281 return(TCL_OK);
1283 else if(!strcmp(s1, "aggtabstate")){
1284 Tcl_SetResult(interp, ps_global->VAR_WP_AGGSTATE ?
1285 ps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE);
1286 return(TCL_OK);
1288 else if(!strcmp(s1, "alpinestate")){
1289 char *wps, *p, *q;
1291 if((wps = ps_global->VAR_WP_STATE) != NULL){
1292 wps = p = q = cpystr(wps);
1294 if(*q == '\\' && *(q+1) == '$')
1295 q++;
1296 while((*p++ = *q++) != '\0');
1299 Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE);
1301 if(wps)
1302 fs_give((void **) &wps);
1304 return(TCL_OK);
1306 else if(!strcmp(s1, "foreground")){
1307 char *color;
1309 if(!((color = pico_get_last_fg_color())
1310 && (color = color_to_asciirgb(color))
1311 && (color = peColorStr(color,tmp_20k_buf))))
1312 color = "000000";
1314 Tcl_SetResult(interp, color, TCL_VOLATILE);
1315 return(TCL_OK);
1317 else if(!strcmp(s1, "background")){
1318 char *color;
1320 if(!((color = pico_get_last_bg_color())
1321 && (color = color_to_asciirgb(color))
1322 && (color = peColorStr(color,tmp_20k_buf))))
1323 color = "FFFFFF";
1325 Tcl_SetResult(interp, color, TCL_VOLATILE);
1326 return(TCL_OK);
1328 else if(!strcmp(s1, "flaglist")){
1329 int i;
1330 char *p;
1331 Tcl_Obj *itemObj;
1334 * BUG: This list should get merged with the static list in "cmd_flag"
1335 * and exported via some function similar to "feature_list()"
1337 static char *flag_list[] = {
1338 "Important", "New", "Answered", "Deleted", NULL
1342 * CMD: flaglist
1344 * Returns: list of FLAGS available for setting
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)
1354 return(TCL_OK);
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;
1363 * CMD: featurelist
1365 * Returns: list of FEATURES available for setting
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);
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)
1386 curfeature = s;
1389 if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1390 if(Tcl_ListObjAppendElement(interp,
1391 resObj,
1392 itemObj) != TCL_OK)
1397 if(resObj){
1398 Tcl_ListObjAppendElement(interp, secObj, resObj);
1399 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj);
1402 return(TCL_OK);
1404 else if(!strcmp(s1, "featuresettings")){
1405 int i;
1406 FEATURE_S *feature;
1407 Tcl_Obj *itemObj;
1410 * CMD: featuresettings
1412 * Returns: list of FEATURES currently SET
1414 for(i = 0; (feature = feature_list(i)); i++)
1415 if(feature_list_section(feature)){
1416 if(F_ON(feature->id, ps_global)){
1417 if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1418 if(Tcl_ListObjAppendElement(interp,
1419 Tcl_GetObjResult(interp),
1420 itemObj) != TCL_OK)
1426 return(TCL_OK);
1428 else if(!strcmp(s1, "signature")){
1429 char *sig;
1431 if((ps_global->VAR_LITERAL_SIG
1432 || (ps_global->VAR_SIGNATURE_FILE
1433 && IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)))
1434 && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){
1435 char *p, *q;
1437 for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1438 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1439 Tcl_NewStringObj(p, q - p));
1441 if(*p)
1442 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1443 Tcl_NewStringObj(p, -1));
1445 fs_give((void **) &sig);
1447 else
1448 Tcl_SetResult(interp, "", TCL_STATIC);
1450 return(TCL_OK);
1452 else if(!strcmp(s1, "rawsig")){
1453 char *err = NULL, *sig = NULL, *p, *q;
1455 if(ps_global->VAR_LITERAL_SIG){
1456 char *err = NULL;
1457 char **apval;
1459 if(ps_global->restricted){
1460 err = "Alpine demo can't change config file";
1462 else{
1463 /* BUG: no "exceptions file" support */
1464 if((apval = APVAL(&ps_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);
1469 else
1470 err = "Problem accessing configuration";
1473 else if(!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE))
1474 snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
1475 ps_global->VAR_SIGNATURE_FILE ? ps_global->VAR_SIGNATURE_FILE : "<null>");
1476 else if(!(sig = simple_read_remote_file(ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))
1477 err = "Can't read remote pinerc";
1479 if(err){
1480 Tcl_SetResult(interp, err, TCL_VOLATILE);
1481 return(TCL_ERROR);
1484 for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1485 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1486 Tcl_NewStringObj(p, q - p));
1488 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1489 Tcl_NewStringObj(p, -1));
1490 fs_give((void **) &sig);
1491 return(TCL_OK);
1493 else if(!strcmp(s1, "statmsg")){
1494 char *s = sml_getmsg();
1495 /* BUG: can this be removed? */
1497 Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE);
1498 return(TCL_OK);
1500 else if(!strcmp(s1, "statmsgs")){
1501 char **s = sml_getmsgs();
1502 char **tmps, *lmsg = NULL;
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));
1510 fs_give((void **)&s);
1511 return(TCL_OK);
1513 else if(!strcmp(s1, "saveconf")){
1514 write_pinerc(ps_global, Main, WRP_NOUSER);
1515 return(TCL_OK);
1517 else if(!strucmp(s1, "sort")){
1518 return(peAppendDefaultSort(interp));
1520 else if(!strcmp(s1, "ldapenabled")){
1522 * CMD: ldapenabled
1524 * Returns: 1 if enabled 0 if not
1526 #ifdef ENABLE_LDAP
1527 Tcl_SetResult(interp, "1", TCL_VOLATILE);
1528 #else
1529 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1530 #endif
1532 return(TCL_OK);
1534 else if(!strcmp(s1, "prunecheck")){
1535 time_t now;
1536 struct tm *tm_now;
1537 char tmp[50];
1539 if(!check_prune_time(&now, &tm_now)){
1540 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1541 return(TCL_OK);
1542 } else {
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
1553 ps_global->last_expire_year = tm_now->tm_year;
1554 ps_global->last_expire_month = tm_now->tm_mon;
1555 snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
1556 ps_global->last_expire_month + 1);
1557 set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main);
1559 Tcl_SetResult(interp, "1", TCL_VOLATILE);
1561 return(TCL_OK);
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;
1572 now = time((time_t *)0);
1573 tm_now = localtime(&now);
1574 cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
1576 if(!(prune_cntxt = default_save_context(ps_global->context_list)))
1577 prune_cntxt = ps_global->context_list;
1579 if(prune_cntxt){
1580 if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC
1581 && context_isambig(ps_global->VAR_DEFAULT_FCC))
1582 if((retObj = wp_prune_folders(prune_cntxt,
1583 ps_global->VAR_DEFAULT_FCC,
1584 cur_month, "sent",
1585 ps_global->pruning_rule, &ok,
1586 moved_fldrs, interp)) != NULL)
1587 Tcl_ListObjAppendElement(interp,
1588 Tcl_GetObjResult(interp),
1589 retObj);
1591 if(ok && ps_global->VAR_READ_MESSAGE_FOLDER
1592 && *ps_global->VAR_READ_MESSAGE_FOLDER
1593 && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER))
1594 if((retObj = wp_prune_folders(prune_cntxt,
1595 ps_global->VAR_READ_MESSAGE_FOLDER,
1596 cur_month, "read",
1597 ps_global->pruning_rule, &ok,
1598 moved_fldrs, interp)) != NULL)
1599 Tcl_ListObjAppendElement(interp,
1600 Tcl_GetObjResult(interp),
1601 retObj);
1602 if(ok && (p = ps_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 ps_global->pruning_rule, &ok,
1608 moved_fldrs, interp)) != NULL)
1609 Tcl_ListObjAppendElement(interp,
1610 Tcl_GetObjResult(interp),
1611 retObj);
1614 moved_fldrs = 1;
1615 return(TCL_OK);
1617 else if(!strcmp(s1, "authrequestor")){
1618 Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC);
1619 return(TCL_OK);
1621 else if(!strcmp(s1, "noop")){
1622 /* tickle the imap server too */
1623 if(ps_global->mail_stream)
1624 pine_mail_ping(ps_global->mail_stream);
1626 Tcl_SetResult(interp, "NOOP", TCL_STATIC);
1627 return(TCL_OK);
1629 else if(!strcmp(s1, "inputtimeout")){
1630 Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE);
1631 return(TCL_OK);
1634 else if(objc == 3){
1635 if(!strcmp(s1, "feature")){
1636 char *featurename;
1637 int i, isset = 0;
1638 FEATURE_S *feature;
1641 * CMD: feature
1643 * ARGS: featurename -
1645 * Returns: 1 if named feature set, 0 otherwise
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, ps_global);
1652 break;
1655 Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE);
1656 return(TCL_OK);
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);
1665 if(strcmp("viewer-hdr-colors", varname) == 0){
1666 SPEC_COLOR_S *hcolors, *thc;
1667 Tcl_Obj *resObj;
1668 char hexcolor[256], *tstr = NULL;
1670 hcolors = spec_colors_from_varlist(ps_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);
1689 fs_give((void **)&hcolors);
1690 return(TCL_OK);
1692 else {
1693 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
1694 for(vtmp = &ps_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));
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 = &ps_global->vars[V_NORM_FORE_COLOR];
1711 vtmp->name && strucmp(vtmp->name, tvname);
1712 vtmp++)
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));
1726 return(TCL_OK);
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;
1736 * CMD: varget
1738 * Returns: get the values for the requested variable
1740 * The list returned follows this general form:
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)
1748 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
1749 Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj",
1750 TCL_VOLATILE);
1751 return(TCL_ERROR);
1754 for(vtmp = ps_global->vars;
1755 vtmp->name && strucmp(vtmp->name, varname);
1756 vtmp++)
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);
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);
1780 else{
1781 itemObj = Tcl_NewStringObj(vtmp->current_val.p ?
1782 vtmp->current_val.p : "", -1);
1783 Tcl_ListObjAppendElement(interp, resObj, itemObj);
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 - ps_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;
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);
1835 itemObj = Tcl_NewStringObj("listbox", -1);
1837 else
1838 itemObj = Tcl_NewStringObj("text", -1);
1840 Tcl_ListObjAppendElement(interp,
1841 Tcl_GetObjResult(interp),
1842 itemObj);
1843 Tcl_ListObjAppendElement(interp,
1844 Tcl_GetObjResult(interp),
1845 secObj);
1847 return(TCL_OK);
1849 else if(!strcmp(s1, "rawsig")){
1851 if(ps_global->VAR_LITERAL_SIG){
1852 char *cstring_version, *sig, *line;
1853 int i, nSig;
1854 Tcl_Obj **objSig;
1856 tmp_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(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
1862 sig = cpystr(tmp_20k_buf);
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);
1869 fs_give((void **) &sig);
1870 return(TCL_OK);
1872 else
1873 return(peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE,
1874 &((Tcl_Obj **)objv)[2]));
1876 else if(!strcmp(s1, "statmsg")){
1877 char *msg;
1880 * CMD: statmsg
1882 * ARGS: msg - text to set
1884 * Returns: nothing, but with global status message
1885 * buf set to given msg
1888 if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
1889 sml_addmsg(0, msg);
1891 return(TCL_OK);
1893 else if(!strcmp(s1, "mode")){
1894 char *mode;
1895 int rv = 0;
1898 * CMD: mode
1900 * ARGS: <mode>
1902 * Returns: return value of given binary mode
1905 if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1906 if(!strcmp(mode, "full-header-mode"))
1907 rv = ps_global->full_header;
1910 Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
1911 return(TCL_OK);
1913 else if(!strcmp(s1, "indexlines")){
1914 int n;
1915 char *p;
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);
1921 return(TCL_OK);
1923 else if(!strcmp(s1, "aggtabstate")){
1924 int n;
1925 char *p;
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);
1931 return(TCL_OK);
1933 else if(!strcmp(s1, "alpinestate")){
1934 char *wps, *p, *q, *twps = NULL;
1935 int dollars = 0;
1937 if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1938 for(p = wps; *p; p++)
1939 if(*p == '$')
1940 dollars++;
1942 if(dollars){
1943 twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char));
1944 p = wps;
1945 q = twps;
1947 if(*p == '$')
1948 *q++ = '\\';
1950 while((*q++ = *p++) != '\0');
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);
1959 return(TCL_OK);
1961 else if(!strcmp(s1, "set")){
1962 Tcl_Obj *rObj;
1964 if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
1965 Tcl_SetObjResult(interp, rObj);
1966 return(TCL_OK);
1968 else
1969 return(TCL_ERROR);
1971 else if(!strcmp(s1, "unset")){
1972 char *varname;
1974 return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR);
1977 else if(objc == 4){
1978 if(!strcmp(s1, "feature")){
1979 char *featurename;
1980 int i, set, wasset = 0;
1981 FEATURE_S *feature;
1984 * CMD: feature
1986 * ARGS: featurename -
1987 * value - new value to assign flag
1989 * Returns: 1 if named feature set, 0 otherwise
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, ps_global)){
1997 toggle_feature(ps_global,
1998 &ps_global->vars[V_FEATURE_LIST],
1999 feature, TRUE, Main);
2001 if(ps_global->prc)
2002 ps_global->prc->outstanding_pinerc_changes = 1;
2005 break;
2008 Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
2009 return(TCL_OK);
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;
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);
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);
2032 if(strucmp(function, "plain") == 0){
2033 if((text = help_name2section(helpname, strlen(helpname)))
2034 == NO_HELP)
2035 return(TCL_OK);
2037 else if(strucmp(function, "variable") == 0){
2038 for(vtmp = ps_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);
2047 text = config_help(vtmp - ps_global->vars, 0);
2048 if(text == NO_HELP)
2049 return(TCL_OK);
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;
2058 if(!ftmp || text == NO_HELP){
2059 return(TCL_OK);
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);
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);
2076 return(TCL_OK);
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;
2084 if((varname = Tcl_GetStringFromObj(objv[2], NULL))
2085 && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
2086 &objVal) == TCL_OK)){
2087 for(vtmp = ps_global->vars;
2088 vtmp->name && strucmp(vtmp->name, varname);
2089 vtmp++);
2090 if(!vtmp->name){
2091 return(TCL_ERROR);
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);
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);
2116 set_current_val(vtmp, FALSE, FALSE);
2117 return(TCL_OK);
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);
2133 return(TCL_ERROR);
2135 else if(!strcmp(s1, "mode")){
2136 char *mode;
2137 int value, rv = 0;
2140 * CMD: mode
2142 * ARGS: <mode> <value>
2144 * Returns: old value of binary mode we were told to set
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 = ps_global->full_header;
2151 ps_global->full_header = value;
2155 Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
2156 return(TCL_OK);
2158 else if(!strcmp(s1, "set")){
2159 Tcl_Obj *rObj;
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);
2165 else
2166 return(TCL_ERROR);
2169 else
2170 err = "PEInfo: Too many arguments";
2174 Tcl_SetResult(interp, err, TCL_STATIC);
2175 return(TCL_ERROR);
2180 * PEConfigCmd - edit various alpine config variables
2182 * The goal here is to remember what's changed, but not write to pinerc
2183 * until the user's actually chosen to save.
2186 PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2188 char *err = "Unknown PEConfig request";
2189 char *s1;
2191 dprint((2, "PEConfigCmd"));
2193 if(objc == 1){
2194 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
2195 Tcl_SetResult(interp, err, TCL_STATIC);
2196 return(TCL_ERROR);
2198 s1 = Tcl_GetStringFromObj(objv[1], NULL);
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;
2209 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
2210 Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
2211 return(TCL_ERROR);
2214 if(!strcmp(varname, "viewer-hdr-colors")){
2215 char *newhdr = NULL, *newpat = NULL, *utype;
2216 int hindex, i;
2218 if(objc < 5){
2219 Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
2220 return(TCL_ERROR);
2223 if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
2224 hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
2225 else
2226 hcolors = spec_colors_from_varlist(ps_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);
2232 if(!strcmp(utype, "delete")){
2233 if(!hcolors){
2234 Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
2235 return(TCL_ERROR);
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);
2243 if(hindex == 0){
2244 thc = hcolors;
2245 hcolors = hcolors->next;
2246 thc->next = NULL;
2247 free_spec_colors(&thc);
2249 else{
2250 /* zero based */
2251 for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
2254 if(thc && thc->next){
2255 SPEC_COLOR_S *thc2 = thc->next;
2257 thc->next = thc2->next;
2258 thc2->next = NULL;
2259 free_spec_colors(&thc2);
2261 else{
2262 Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
2263 return(TCL_ERROR);
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);
2273 if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
2274 return (TCL_ERROR);
2276 if(cObjc != 2){
2277 Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
2278 return(TCL_ERROR);
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);
2286 if(cObjc != 2){
2287 Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
2288 return(TCL_ERROR);
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;
2296 for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
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");
2305 if(newpat && *newpat)
2306 (*hcp)->val = string_to_pattern(newpat);
2307 else
2308 (*hcp)->val = NULL;
2310 (*hcp)->next = NULL;
2312 else{
2313 Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
2314 return(TCL_ERROR);
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);
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);
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);
2340 for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
2343 if(!thc){
2344 Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
2345 return (TCL_ERROR);
2348 if(thc->spec)
2349 fs_give((void **)&thc->spec);
2351 thc->spec = cpystr(newhdr);
2352 if(ascii_colorstr(asciicolor, fghex) == 0) {
2353 if(thc->fg)
2354 fs_give((void **)&thc->fg);
2356 thc->fg = cpystr(asciicolor);
2358 else{
2359 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
2360 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2361 return(TCL_ERROR);
2364 if(ascii_colorstr(asciicolor, bghex) == 0) {
2365 if(thc->bg)
2366 fs_give((void **)&thc->bg);
2368 thc->bg = cpystr(asciicolor);
2370 else{
2371 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2372 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2373 return(TCL_ERROR);
2376 if(thc->val)
2377 fs_give((void **)&thc->val);
2379 if(newpat && *newpat){
2380 thc->val = string_to_pattern(newpat);
2383 else{
2384 Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
2385 return(TCL_ERROR);
2388 vtmp = &ps_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]);
2392 if(vtmp->changed_val.l)
2393 fs_give((void **)&vtmp->changed_val.l);
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);
2400 else {
2401 if(objc != 4){
2402 Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
2403 return(TCL_ERROR);
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);
2414 snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
2415 for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
2416 vtmp->name && strucmp(vtmp->name, tvname);
2417 vtmp++)
2420 if(!vtmp->name || vtmp->is_list){
2421 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2422 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2423 return(TCL_ERROR);
2426 if(ascii_colorstr(asciicolor, fghex) == 0) {
2427 if(vtmp->changed_val.p)
2428 fs_give((void **)&vtmp->changed_val.p);
2430 vtmp->changed_val.p = cpystr(asciicolor);
2431 vtmp->is_changed_val = 1;
2433 /* We need to handle this in the actual config setting
2434 * if(!strucmp(varname, "normal"))
2435 * pico_set_fg_color(asciicolor);
2438 else{
2439 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
2440 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2441 return(TCL_ERROR);
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 = &ps_global->vars[V_NORM_FORE_COLOR];
2448 vtmp->name && strucmp(vtmp->name, tvname);
2449 vtmp++)
2452 if(!vtmp->name || vtmp->is_list){
2453 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2454 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2455 return(TCL_ERROR);
2458 if(ascii_colorstr(asciicolor, bghex) == 0) {
2459 if(vtmp->changed_val.p)
2460 fs_give((void **)&vtmp->changed_val.p);
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);
2469 else{
2470 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2471 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
2472 return(TCL_ERROR);
2475 Tcl_SetResult(interp, "1", TCL_STATIC);
2476 return(TCL_OK);
2479 else if(!strcmp(s1, "ruleset")){
2480 return(peRuleSet(interp, &((Tcl_Obj **)objv)[2]));
2482 else if(objc == 2){
2483 if(!strcmp(s1, "featuresettings")){
2484 struct variable *vtmp;
2485 int i;
2486 FEATURE_S *feature;
2488 vtmp = &ps_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, ps_global)){
2493 Tcl_ListObjAppendElement(interp,
2494 Tcl_GetObjResult(interp),
2495 Tcl_NewStringObj(feature->name, -1));
2498 return(TCL_OK);
2500 else if(!strcmp(s1, "rawsig")){
2501 char *err = NULL, *sig = NULL, *p, *q;
2502 int i;
2503 struct variable *vtmp;
2505 vtmp = &ps_global->vars[V_LITERAL_SIG];
2506 if(vtmp->is_changed_val ? vtmp->changed_val.p
2507 : ps_global->VAR_LITERAL_SIG){
2508 char *err = NULL;
2509 char **apval;
2511 if(ps_global->restricted){
2512 err = "Alpine demo can't change config file";
2514 else{
2515 /* BUG: no "exceptions file" support */
2516 apval = (vtmp->is_changed_val ? &vtmp->changed_val.p
2517 : APVAL(&ps_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);
2523 else
2524 err = "Problem accessing configuration";
2527 else if((vtmp = &ps_global->vars[V_SIGNATURE_FILE])
2528 && !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p
2529 : ps_global->VAR_SIGNATURE_FILE))
2530 snprintf(err = tmp_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 : (ps_global->VAR_SIGNATURE_FILE
2534 ? ps_global->VAR_SIGNATURE_FILE : "<null>"));
2535 else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val
2536 ? vtmp->changed_val.p
2537 : ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))))
2538 err = "Can't read remote pinerc";
2540 if(err){
2541 Tcl_SetResult(interp, err, TCL_VOLATILE);
2542 return(TCL_ERROR);
2545 if(peTSig){
2546 for(i = 0; peTSig[i]; i++)
2547 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2548 Tcl_NewStringObj(peTSig[i],-1));
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));
2555 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2556 Tcl_NewStringObj(p, -1));
2557 fs_give((void **) &sig);
2559 return(TCL_OK);
2561 else if(!strcmp(s1, "filters")){
2562 long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
2563 PAT_STATE pstate;
2564 PAT_S *pat;
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));
2575 return(TCL_OK);
2577 else if(!strcmp(s1, "scores")){
2578 long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
2579 PAT_STATE pstate;
2580 PAT_S *pat;
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));
2591 return(TCL_OK);
2593 else if(!strcmp(s1, "indexcolors")){
2594 long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
2595 PAT_STATE pstate;
2596 PAT_S *pat;
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));
2607 return(TCL_OK);
2609 else if(!strcmp(s1, "collections")){
2610 struct variable *vtmp;
2611 int i;
2612 CONTEXT_S *new_ctxt;
2614 vtmp = &ps_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);
2633 vtmp = &ps_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);
2653 return(TCL_OK);
2655 else if(!strcmp(s1, "newconf")){
2656 struct variable *vtmp;
2657 int i;
2658 FEATURE_S *feature;
2660 for(vtmp = ps_global->vars; vtmp->name; vtmp++)
2661 vtmp->is_changed_val = 0;
2663 for(i = 0; (feature = feature_list(i)); i++)
2664 F_CH_SET(feature->id, F_ON(feature->id, ps_global));
2666 if(peTSig){
2667 for(i = 0; peTSig[i]; i++)
2668 fs_give((void **)&peTSig[i]);
2669 fs_give((void **)&peTSig);
2672 close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED);
2673 return(TCL_OK);
2675 else if(!strcmp(s1, "saveconf")){
2676 struct variable *vtmp;
2677 int i, did_change = 0, def_sort_rev;
2678 FEATURE_S *feature;
2680 if(ps_global->vars[V_FEATURE_LIST].is_changed_val){
2681 ps_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, ps_global)){
2685 did_change = 1;
2686 toggle_feature(ps_global,
2687 &ps_global->vars[V_FEATURE_LIST],
2688 feature, TRUE, Main);
2693 for(vtmp = ps_global->vars; vtmp->name; vtmp++){
2694 if(vtmp->is_changed_val
2695 && (vtmp - ps_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;
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;
2711 set_current_val(vtmp, FALSE, FALSE);
2712 vtmp->is_changed_val = 0;
2713 did_change = 1;
2714 switch (vtmp - ps_global->vars) {
2715 case V_USER_DOMAIN:
2716 init_hostname(ps_global);
2717 case V_FOLDER_SPEC:
2718 case V_NEWS_SPEC:
2719 free_contexts(&ps_global->context_list);
2720 init_folders(ps_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(ps_global->VAR_INDEX_FORMAT,
2737 &ps_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(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev);
2759 break;
2760 case V_VIEW_HDR_COLORS :
2761 set_custom_spec_colors(ps_global);
2762 break;
2763 case V_POST_CHAR_SET :
2764 update_posting_charset(ps_global, 1);
2765 break;
2766 default:
2767 break;
2771 if(peTSig){
2772 peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE, NULL);
2774 if(did_change){
2775 if(write_pinerc(ps_global, Main, WRP_NOUSER) == 0)
2776 q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!");
2778 return(TCL_OK);
2780 else if(!strcmp(s1, "columns")){
2781 Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
2782 return(TCL_OK);
2784 else if(!strcmp(s1, "indextokens")){
2785 INDEX_PARSE_T *tok;
2786 int i;
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));
2793 return(TCL_OK);
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;
2805 if(varname == NULL) return(TCL_ERROR);
2807 for(vtmp = ps_global->vars;
2808 vtmp->name && strucmp(vtmp->name, varname);
2809 vtmp++)
2812 if(!vtmp->name){
2813 Tcl_SetResult(interp, err, TCL_VOLATILE);
2814 return(TCL_ERROR);
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));
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));
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));
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));
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 - ps_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;
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));
2895 input_type = cpystr("listbox");
2897 else
2898 input_type = cpystr("text");
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);
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;
2923 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2924 return(TCL_ERROR);
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++);
2932 if(!pat)
2933 return(TCL_ERROR);
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);
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);
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);
2958 else return(TCL_ERROR);
2960 return(TCL_OK);
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;
2969 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2970 return(TCL_ERROR);
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++);
2978 if(!pat)
2979 return(TCL_ERROR);
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);
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);
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);
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,tmp_20k_buf))))
3006 color = "";
3008 Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
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,tmp_20k_buf))))
3015 color = "";
3017 Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
3018 Tcl_ListObjAppendElement(interp, tObj, colObj);
3020 Tcl_ListObjAppendElement(interp, resObj, tObj);
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);
3027 else return(TCL_ERROR);
3029 return(TCL_OK);
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;
3039 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
3040 return(TCL_ERROR);
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++);
3048 if(!pat)
3049 return(TCL_ERROR);
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);
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);
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);
3069 peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : "");
3071 if(hdr)
3072 fs_give((void **) &hdr);
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);
3079 else return(TCL_ERROR);
3081 return(TCL_OK);
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;
3089 vtmp = &ps_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 = &ps_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++);
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';
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);
3142 return(TCL_OK);
3144 else
3145 return(TCL_ERROR);
3147 else if(!strcmp(s1, "rawsig")){
3148 struct variable *vtmp;
3149 char *cstring_version, *sig, *line;
3150 int i, nSig;
3151 Tcl_Obj **objSig;
3153 vtmp = &ps_global->vars[V_LITERAL_SIG];
3154 if(vtmp->is_changed_val ? vtmp->changed_val.p
3155 : ps_global->VAR_LITERAL_SIG){
3157 tmp_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(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
3163 sig = cpystr(tmp_20k_buf);
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;
3172 fs_give((void **) &sig);
3173 return(TCL_OK);
3175 else {
3176 if(peTSig){
3177 for(i = 0; peTSig[i]; i++)
3178 fs_give((void **)&peTSig[i]);
3179 fs_give((void **)&peTSig);
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 : "");
3187 peTSig[i] = NULL;
3188 return(TCL_OK);
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);
3198 if(strcmp("viewer-hdr-colors", varname) == 0){
3199 SPEC_COLOR_S *hcolors, *thc;
3200 Tcl_Obj *resObj;
3201 char hexcolor[256], *tstr = NULL;
3203 if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
3204 hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
3205 else
3206 hcolors = spec_colors_from_varlist(ps_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);
3225 fs_give((void **)&hcolors);
3226 return(TCL_OK);
3228 else {
3229 char *colorp;
3231 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
3233 for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
3234 vtmp->name && strucmp(vtmp->name, tvname);
3235 vtmp++)
3238 if(!vtmp->name) return(TCL_ERROR);
3239 if(vtmp->is_list) return(TCL_ERROR);
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;
3246 if(colorp){
3247 hex_colorstr(hexcolor, colorp);
3248 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3249 Tcl_NewStringObj(hexcolor, -1));
3251 else
3252 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3253 Tcl_NewStringObj("", -1));
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 = &ps_global->vars[V_NORM_FORE_COLOR];
3259 vtmp->name && strucmp(vtmp->name, tvname);
3260 vtmp++)
3263 if(!vtmp->name) return(TCL_ERROR);
3264 if(vtmp->is_list) return(TCL_ERROR);
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;
3271 if(colorp){
3272 hex_colorstr(hexcolor, colorp);
3273 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3274 Tcl_NewStringObj(hexcolor, -1));
3276 else
3277 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3278 Tcl_NewStringObj("", -1));
3280 return(TCL_OK);
3282 else if(!strcmp(s1, "cldel")){
3283 int cl, i, j, n;
3284 struct variable *vtmp;
3285 char **newl;
3287 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3288 return(TCL_ERROR);
3289 vtmp = &ps_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 = &ps_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;
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]);
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;
3326 return(TCL_OK);
3328 else if(!strcmp(s1, "columns")){
3329 int n;
3330 char *p;
3332 if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR
3333 && n >= MIN_SCREEN_COLS
3334 && n < (MAX_SCREEN_COLS - 1)
3335 && ps_global->ttyo->screen_cols != n){
3336 clear_index_cache(sp_inbox_stream(), 0);
3337 ps_global->ttyo->screen_cols = n;
3338 set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main);
3339 Tcl_SetResult(interp, p, TCL_VOLATILE);
3341 else
3342 Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
3344 return(TCL_OK);
3346 else if(!strcmp(s1, "reset")){
3347 char *p;
3349 if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
3350 if(!strcmp(p,"pinerc")){
3351 struct variable *var;
3352 PINERC_S *prc;
3354 /* new pinerc structure, copy location pointers */
3355 prc = new_pinerc_s(ps_global->prc->name);
3356 prc->type = ps_global->prc->type;
3357 prc->rd = ps_global->prc->rd;
3358 prc->outstanding_pinerc_changes = 1;
3360 /* tie off original pinerc struct and free it */
3361 ps_global->prc->rd = NULL;
3362 ps_global->prc->outstanding_pinerc_changes = 0;
3363 free_pinerc_s(&ps_global->prc);
3365 /* set global->prc to new struct with no pinerc_lines
3366 * and fool write_pinerc into not writing changed vars
3368 ps_global->prc = prc;
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
3375 for(var = ps_global->vars; var->name != NULL; var++){
3376 var->been_written = ((var - ps_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);
3381 else{
3382 fs_give((void **)&var->main_user_val.p);
3383 fs_give((void **)&var->global_val.p);
3387 write_pinerc(ps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN);
3389 peInitVars(ps_global);
3390 return(TCL_OK);
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;
3403 if(varname == NULL) return(TCL_ERROR);
3404 for(vtmp = ps_global->vars;
3405 vtmp->name && strucmp(vtmp->name, varname);
3406 vtmp++)
3408 if(!vtmp->name){
3409 Tcl_SetResult(interp, err, TCL_VOLATILE);
3410 return(TCL_ERROR);
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);
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);
3433 if(tstrlist)
3434 tstrlist[strlistpos] = NULL;
3435 vtmp->changed_val.l = tstrlist;
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';
3448 else if(tline[0] == '\0'){
3449 fs_give((void **)&tline);
3451 if(tline){
3452 vtmp->changed_val.p = cpystr(tline);
3453 fs_give((void **)&tline);
3456 else
3457 vtmp->changed_val.p = cpystr("");
3460 return(TCL_OK);
3462 else if(!strcmp(s1, "feature")){
3463 char *featurename;
3464 int i, set, wasset = 0;
3465 FEATURE_S *feature;
3468 * CMD: feature
3470 * ARGS: featurename -
3471 * value - new value to assign flag
3473 * Returns: 1 if named feature set, 0 otherwise
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 ps_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;
3486 Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
3487 return(TCL_OK);
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;
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 = &ps_global->vars[V_FOLDER_SPEC];
3505 nvar = &ps_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;
3514 else if(cl >= fvarn && cl < nvarn + fvarn){
3515 vtmp = nvar;
3516 icnt = cl - fvarn;
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;
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;
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;
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;
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;
3600 return(TCL_OK);
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;
3610 if(!strcmp(s1, "cladd")) add = 1;
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;
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);
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 = &ps_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 = &ps_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;
3658 else {
3659 vtmp = &ps_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 = &ps_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;
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]);
3688 newl[j] = NULL;
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;
3698 else
3699 err = "PEInfo: Too many arguments";
3701 Tcl_SetResult(interp, err, TCL_STATIC);
3702 return(TCL_ERROR);
3707 peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv)
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;
3715 if(!(file && IS_REMOTE(file))){
3716 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
3717 file ? file : "<null>");
3718 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3719 return(TCL_ERROR);
3723 * We could parse the name here to find what type it is. So far we
3724 * only have type RemImap.
3726 rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE,
3727 NULL, "Error: ", "Can't fetch remote signature.");
3728 if(!rd){
3729 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file);
3730 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3731 return(TCL_ERROR);
3734 try_cache = rd_read_metadata(rd);
3736 if(rd->access == MaybeRorW){
3737 if(rd->read_status == 'R')
3738 rd->access = ReadOnly;
3739 else
3740 rd->access = ReadWrite;
3743 if(rd->access != NoExists){
3745 rd_check_remvalid(rd, 1L);
3748 * If the cached info says it is readonly but
3749 * it looks like it's been fixed now, change it to readwrite.
3751 if(rd->read_status == 'R'){
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.
3757 rd_check_readonly_access(rd);
3758 if(rd->read_status == 'W'){
3759 rd->access = ReadWrite;
3760 rd->flags |= REM_OUTOFDATE;
3762 else{
3763 rd_close_remdata(&rd);
3764 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file);
3765 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3766 return(TCL_ERROR);
3770 if(rd->flags & REM_OUTOFDATE){
3771 if(rd_update_local(rd) != 0){
3773 dprint((1, "pinerc_remote_open: rd_update_local failed"));
3775 * Don't give up altogether. We still may be
3776 * able to use a cached copy.
3779 else{
3780 dprint((7, "%s: copied remote to local (%ld)",
3781 rd->rn, (long)rd->last_use));
3785 if(rd->access == ReadWrite)
3786 rd->flags |= DO_REMTRIM;
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(tmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file);
3793 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3794 return(TCL_ERROR);
3797 unlink(rd->lf);
3799 sig = NULL;
3800 tmp_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(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
3806 SIG_MAX_COLS, line);
3809 else if(peTSig){
3810 for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) {
3811 snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
3812 SIG_MAX_COLS, peTSig[i]);
3814 for(i = 0; peTSig[i]; i++)
3815 fs_give((void **)&peTSig[i]);
3816 fs_give((void **)&peTSig);
3818 else
3819 return(TCL_ERROR);
3821 sig = cpystr(tmp_20k_buf);
3823 if((fp = fopen(rd->lf, "w")) != NULL)
3824 n = fwrite(sig, strlen(sig), 1, fp);
3826 fs_give((void **) &sig);
3828 if(fp){
3829 if(n != 1){
3830 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s",
3831 rd->lf, error_description(errno));
3832 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3833 rd_close_remdata(&rd);
3836 fclose(fp);
3837 if(n != 1)
3838 return(TCL_ERROR);
3840 else {
3841 rd_close_remdata(&rd);
3842 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s",
3843 rd->lf, error_description(errno));
3844 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3845 return(TCL_ERROR);
3848 datebuf[0] = '\0';
3850 if(!rd->t.i.stream){
3851 long retflags = 0;
3853 rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags);
3856 if((e = rd_update_remote(rd, datebuf)) != 0){
3857 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s",
3858 rd->lf, error_description(errno));
3859 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
3860 rd_close_remdata(&rd);
3861 return(TCL_ERROR);
3864 rd_update_metadata(rd, datebuf);
3865 rd->read_status = 'W';
3866 rd_close_remdata(&rd);
3867 return(TCL_OK);
3872 NAMEVAL_S *sort_key_rules(index)
3873 int index;
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}
3896 return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3897 ? &is_rules[index] : NULL);
3900 NAMEVAL_S *wp_indexheight_rules(index)
3901 int index;
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}
3911 return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3912 ? &is_rules[index] : NULL);
3917 * PEDebugCmd - turn on/off and set various debugging options
3920 PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
3922 char *s;
3924 if(!--objc){ /* only one arg? */
3925 Tcl_WrongNumArgs(interp, 1, objv, "?args?");
3927 else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
3928 if(!strucmp(s, "level")){
3929 if(objc == 2){
3930 int level;
3932 if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3933 return(TCL_ERROR);
3935 if(level > 0){
3936 if(level > 10)
3937 level = 10;
3939 debug = level;
3940 dprint((1, "Debug level %d", level));
3942 else{
3943 dprint((1, "PEDebug ending"));
3944 debug = 0;
3948 Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE);
3949 return(TCL_OK);
3951 else if(!strucmp(s, "write")){
3952 if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){
3954 * script debugging has a high priority since
3955 * statements can be added/removed on the fly
3956 * AND are NOT present by default
3958 dprint((SYSDBG_INFO, "SCRIPT: %s", s));
3961 return(TCL_OK);
3963 else if(!strucmp(s, "imap")){
3964 int level;
3966 if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3967 return(TCL_ERROR);
3969 if(level == 0){
3970 if(ps_global){
3971 ps_global->debug_imap = 0;
3972 if(ps_global->mail_stream)
3973 mail_nodebug(ps_global->mail_stream);
3976 else if(level > 0 && level < 5){
3977 if(ps_global){
3978 ps_global->debug_imap = level;
3979 if(ps_global->mail_stream)
3980 mail_debug(ps_global->mail_stream);
3984 return(TCL_OK);
3986 else
3987 Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC);
3990 return(TCL_ERROR);
3995 * PESessionCmd - Export TCL Session-wide command set
3998 PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
4000 char *op, *err = "Unknown PESession option";
4001 char *pe_user, *pe_host;
4002 int pe_alt, l;
4004 dprint((2, "PESessionCmd"));
4006 if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4007 if(!strcmp(op, "open")){
4008 char *s, *pinerc, *pineconf = NULL;
4011 * CMD: open user remote-pinerc local-default-config
4013 * Initiate a session
4015 * Returns: error string on error, nothing otherwise
4018 if(objc < 4 || objc > 5){
4019 Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc");
4020 return(TCL_ERROR);
4023 if(!(s = Tcl_GetStringFromObj(objv[2], &l))){
4024 Tcl_SetResult(interp, "Unknown User", TCL_STATIC);
4025 return(TCL_ERROR);
4027 else{
4028 int rv;
4030 pe_user = cpystr(s);
4032 #if defined(HAVE_SETENV)
4033 rv = setenv("WPUSER", pe_user, 1);
4034 #elif defined(HAVE_PUTENV)
4036 static char putenvbuf[PUTENV_MAX];
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);
4044 rv = putenv(putenvbuf);
4046 else
4047 rv = 1;
4049 #endif
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);
4060 if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4061 NETMBX mb;
4063 if(mail_valid_net_parse(pinerc, &mb)){
4064 pe_host = cpystr(mb.host);
4065 pe_alt = (mb.sslflag || mb.tlsflag);
4067 else {
4068 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc);
4069 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
4070 return(TCL_ERROR);
4073 else {
4074 Tcl_SetResult(interp, "Unknown config location", TCL_STATIC);
4075 return(TCL_ERROR);
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);
4083 dprint((SYSDBG_INFO, "session (%s) %s - %s",
4084 pe_user, pinerc, pineconf ? pineconf : "<none>"));
4086 /* credential cache MUST already be seeded */
4088 /* destroy old user context */
4089 if(ps_global){
4090 /* destroy open stream */
4091 peDestroyStream(ps_global);
4093 /* destroy old user context */
4094 peDestroyUserContext(&ps_global);
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);
4103 fs_give((void **) &pe_user);
4104 fs_give((void **) &pe_host);
4106 return(TCL_OK);
4108 else if(!strcmp(op, "close")){
4109 if(ps_global){
4110 /* destroy any open stream */
4111 peDestroyStream(ps_global);
4113 /* destroy user context */
4114 peDestroyUserContext(&ps_global);
4117 Tcl_SetResult(interp, "BYE", TCL_STATIC);
4118 return(TCL_OK);
4120 else if(!strcmp(op, "creds")){
4121 char *folder;
4122 int colid;
4124 if(objc < 4){
4125 err = "creds: insufficient args";
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;
4133 * CMD: creds <collection-index> <folder> [user passwd]
4135 * Test for valid credentials to access given folder
4137 * Returns: 1 if so, 0 otherwise
4140 for(i = 0, cp = ps_global ? ps_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;
4147 if(cp){
4148 if(folder[0] == '\0'){
4149 if(cp->use & CNTXT_INCMNG)
4150 rv = 1;
4151 else
4152 folder = "fake-fake";
4154 else if((cp->use & CNTXT_INCMNG)
4155 && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4156 folder = p;
4159 if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4160 NETMBX mb;
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);
4167 if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag)))
4168 rv = 1;
4170 else if(objc == 6){ /* set creds */
4171 char *user, *passwd;
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;
4180 alpine_set_passwd(user, passwd, mb.host,
4181 mb.sslflag
4182 || mb.tlsflag
4183 || (ps_global ? F_ON(F_PREFER_ALT_AUTH, ps_global) : 0));
4184 rv = 1;
4186 else {
4187 err = "creds: unable to read credentials";
4188 break;
4191 else{
4192 err = "creds: invalid args";
4193 break;
4198 (void) Tcl_ListObjAppendElement(interp,
4199 Tcl_GetObjResult(interp),
4200 Tcl_NewIntObj(rv));
4201 return(TCL_OK);
4204 err = "creds: Unrecognized collection ID";
4206 else
4207 err = "creds: failure to acquire folder and collection ID";
4209 else if(!strcmp(op, "nocred")){
4210 char *folder;
4211 int colid;
4213 if(!ps_global){
4214 err = "No Session active";
4216 else if(objc != 4){
4217 err = "nocred: wrong number of args";
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;
4225 * CMD: nocred <collection-index> <folder>
4227 * Test for valid credentials to access given folder
4229 * Returns: 1 if so, 0 otherwise
4232 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
4233 if(i == colid){
4234 int rv = 0;
4235 char tmp[MAILTMPLEN], *p;
4237 if((cp->use & CNTXT_INCMNG)
4238 && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4239 folder = p;
4241 if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4242 NETMBX mb;
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);
4248 alpine_clear_passwd(mb.user, mb.host);
4252 (void) Tcl_ListObjAppendElement(interp,
4253 Tcl_GetObjResult(interp),
4254 Tcl_NewIntObj(rv));
4255 return(TCL_OK);
4258 err = "creds: Unrecognized collection ID";
4260 else
4261 err = "creds: failure to acquire folder and collection ID";
4263 else if(!strcmp(op, "acceptcert")){
4264 char *certhost;
4265 STRLIST_S **p;
4267 if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){
4268 for(p = &peCertHosts; *p; p = &(*p)->next)
4271 *p = new_strlist(certhost);
4274 err = "PESession: no server name";
4276 else if(!strcmp(op, "random")){
4277 if(objc != 3){
4278 err = "PESession: random <length>";
4279 } else {
4280 char s[1025];
4281 int l;
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);
4288 else
4289 err = "PESession: random length too long";
4291 else
4292 err = "PESession: can't get random length";
4295 else if(!strcmp(op, "authdriver")){
4296 if(objc != 4){
4297 err = "PESession: authdriver {add | remove} drivername";
4298 } else {
4299 char *cmd, *driver;
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";
4306 else if(!strcmp(cmd,"disable")){
4307 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){
4308 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver);
4309 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
4310 return(TCL_OK);
4312 else{
4313 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver);
4314 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
4315 return(TCL_ERROR);
4318 else
4319 err = "PESession: unknown authdriver operation";
4321 else
4322 err = "PESession: Can't read driver name";
4324 else
4325 err = "PESesions: Can't read authdriver operation";
4328 else if(!strcmp(op, "abandon")){
4330 * CMD: abandon [timeout]
4332 * Returns: nothing
4335 if(objc != 3){
4336 err = "PESession: abandon [timeout]";
4337 } else {
4338 long t;
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);
4346 else
4347 err = "unrecognized timeout";
4349 else
4350 err = "Can't read timeout";
4353 else if(!strcmp(op, "noexpunge")){
4355 * CMD: noexpunge <state>
4357 * Returns: nothing
4360 if(objc != 3){
4361 err = "PESession: noexpunge <state>";
4362 } else {
4363 int onoff;
4365 if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){
4366 if(onoff == 0 || onoff == 1){
4367 ps_global->noexpunge_on_close = onoff;
4368 return(TCL_OK);
4371 err = "unrecognized on/off state";
4373 else
4374 err = "Can't read on/off state";
4377 else if(!strcmp(op, "setpassphrase")){
4378 #ifdef SMIME
4379 char *passphrase;
4381 if(objc != 3){
4382 err = "PESession: setpassphrase <state>";
4384 else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){
4385 if(ps_global && ps_global->smime){
4386 strncpy((char *) ps_global->smime->passphrase, passphrase,
4387 sizeof(ps_global->smime->passphrase));
4388 ps_global->smime->passphrase[sizeof(ps_global->smime->passphrase)-1] = '\0';
4389 ps_global->smime->entered_passphrase = 1;
4390 ps_global->smime->need_passphrase = 0;
4391 peED.uid = 0;
4392 return(TCL_OK);
4395 #else
4396 err = "S/MIME not configured for this server";
4397 #endif /* SMIME */
4399 else if(!strcmp(op, "expungecheck")) {
4401 * Return open folders and how many deleted messages they have
4403 * return looks something like a list of these:
4404 * {folder-name number-deleted isinbox isincoming}
4406 char *type;
4407 long delete_count;
4408 Tcl_Obj *resObj;
4410 if(objc != 3){
4411 err = "PESession: expungecheck <type>";
4413 else {
4414 type = Tcl_GetStringFromObj(objv[2], NULL);
4415 if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){
4417 if(ps_global->mail_stream != sp_inbox_stream()
4418 || strcmp(type, "current") == 0){
4419 delete_count = count_flagged(ps_global->mail_stream, F_DEL);
4420 resObj = Tcl_NewListObj(0, NULL);
4421 Tcl_ListObjAppendElement(interp, resObj,
4422 Tcl_NewStringObj(pretty_fn(ps_global->cur_folder), -1));
4423 Tcl_ListObjAppendElement(interp, resObj,
4424 Tcl_NewIntObj(delete_count));
4425 Tcl_ListObjAppendElement(interp, resObj,
4426 Tcl_NewIntObj((ps_global->mail_stream
4427 == sp_inbox_stream())
4428 ? 1 : 0));
4429 Tcl_ListObjAppendElement(interp, resObj,
4430 Tcl_NewIntObj((ps_global->context_current->use & CNTXT_INCMNG)
4431 ? 1 : 0));
4432 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4433 resObj);
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);
4449 return(TCL_OK);
4451 else
4452 err = "PESession: expungecheck unknown type";
4455 else if(!strcmp(op, "mailcheck")) {
4457 * CMD: mailcheck
4459 * ARGS: reload -- "1" if we're reloading
4460 * (vs. just checking newmail as a side effect
4461 * of building a new page)
4463 * Return list of folders with new or expunged messages
4465 * return looks something like a list of these:
4466 * {new-count newest-uid announcement-msg}
4468 int reload, force = UFU_NONE, rv;
4469 time_t now = time(0);
4471 if(objc <= 3){
4472 if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
4473 reload = 0;
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();
4482 peED.interp = interp;
4484 /* check for new mail */
4485 new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG);
4487 if(!reload){ /* announced */
4488 zero_new_mail_count();
4491 return(TCL_OK);
4493 else
4494 err = "PESession: mailcheck <reload>";
4498 Tcl_SetResult(interp, err, TCL_STATIC);
4499 return(TCL_ERROR);
4505 * PEFolderChange - create context's directory chain
4506 * corresponding to list of given obj's
4508 * NOTE: caller should call reset_context_folders(cp) to
4509 * clean up data structures this creates before returning
4512 PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[])
4514 int i;
4515 FDIR_S *fp;
4516 char *folder;
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);
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;
4534 return(TCL_OK);
4538 * PEMakeFolderString:
4541 PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath)
4543 int i;
4544 unsigned long size,len;
4545 char *portion,*path;
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);
4555 if(i) size++;
4556 size += strlen(portion);
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;
4568 path[size] = '\0';
4569 if(ppath) *ppath = path; else fs_give((void**) &path);
4570 return(TCL_OK);
4575 * PEFolderCmd - export various bits of folder information
4578 PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
4580 char *op, errbuf[256], *err = "Unknown PEFolder request";
4582 dprint((2, "PEFolderCmd"));
4584 if(objc == 1){
4585 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
4587 else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4588 if(ps_global){
4589 if(objc == 2){
4590 if(!strcmp(op, "current")){
4591 CONTEXT_S *cp;
4592 int i;
4595 * CMD: current
4597 * Returns: string representing the name of the
4598 * current mailbox
4601 for(i = 0, cp = ps_global->context_list; cp && cp != ps_global->context_current; i++, cp = cp->next)
4604 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK
4605 && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->cur_folder,-1)) == TCL_OK)
4606 return(TCL_OK);
4608 return(TCL_ERROR);
4610 else if(!strcmp(op, "collections")){
4611 CONTEXT_S *cp;
4612 int i;
4615 * CMD: collections
4617 * Returns: List of currently configured collections
4619 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next){
4620 Tcl_Obj *objv[3];
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);
4626 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4627 Tcl_NewListObj(3, objv));
4630 return(TCL_OK);
4632 else if(!strcmp(op, "defaultcollection")){
4633 int i;
4634 CONTEXT_S *cp;
4636 for(i = 0, cp = ps_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);
4642 err = "PEFolder: isincoming: Invalid collection ID";
4644 else if(!strcmp(op, "clextended")){
4645 CONTEXT_S *cp;
4646 int i;
4647 char tpath[MAILTMPLEN], *p;
4650 * CMD: clextended
4652 * Returns: Extended list of current collections
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
4664 * had to get rid of this cause the args are changed
4666 * if(strcmp("extended",
4667 * Tcl_GetStringFromObj(objv[2], NULL))){
4668 * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE);
4669 * return(TCL_ERROR);
4672 for(i = 0, cp = ps_global->context_list; cp ;
4673 i++, cp = cp->next){
4674 Tcl_Obj *objv[7];
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';
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));
4704 return(TCL_OK);
4707 else if(objc == 3 && !strcmp(op, "delimiter")){
4708 int colid, i;
4709 char delim[2] = {'\0', '\0'};
4710 CONTEXT_S *cp;
4712 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4713 for(i = 0, cp = ps_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;
4718 break;
4721 Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE);
4722 return(TCL_OK);
4724 else
4725 err = "PEFolder: delimiter: Can't read collection ID";
4727 else if(objc == 3 && !strcmp(op, "isincoming")){
4728 int colid, i;
4729 CONTEXT_S *cp;
4731 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4732 for(i = 0, cp = ps_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);
4738 err = "PEFolder: isincoming: Invalid collection ID";
4740 else
4741 err = "PEFolder: isincoming: Can't read collection ID";
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;
4750 * CMD: unread
4752 * Returns: number of unread messages in given
4753 * folder
4755 if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4756 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
4757 if(i == colid)
4758 break;
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);
4766 else{
4768 * BUG: some sort of caching to prevent open() fore each call?
4769 * does stream cache offset this?
4771 if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4772 && (mstream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
4773 long retflags = 0;
4775 ps_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 ps_global->noshow_error = 0;
4783 count = count_flagged(mstream, flags);
4785 if(our_stream)
4786 pine_mail_close(mstream);
4789 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4790 return(TCL_OK);
4793 else
4794 err = "PEFolder: unread: Invalid collection ID";
4796 else
4797 err = "PEFolder: unread: Can't read collection ID";
4799 else if(objc == 5 && !strcmp(op, "empty")){
4801 * CMD: empty
4803 * Returns: number of expunge messages
4805 * Arguments: <colnum> <folder> <what>
4806 * where <what> is either <uid>, 'selected', or 'all'
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];
4816 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4817 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
4818 if(i == colid) break;
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, ps_global->mail_stream)))
4827 || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){
4828 long retflags = 0;
4830 our_stream = 1;
4831 stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags);
4834 if(stream){
4835 msgmap = sp_msgmap(stream);
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);
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
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);
4860 else
4861 set_lflag(stream, msgmap, uid, MN_STMP, 0);
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);
4869 else
4870 count = 0L;
4872 else{
4873 uid = 0;
4874 for(p = what; *p; p++)
4875 if(isdigit((unsigned char) *p)){
4876 uid = (uid * 10) + (*p - '0');
4878 else{
4879 errstr = "Invalid uid value";
4880 break;
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;
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);
4901 if(our_stream)
4902 pine_mail_close(stream);
4904 else
4905 errstr = "no stream";
4907 else
4908 errstr = "Cannot get which ";
4910 else
4911 errstr = "Cannot get folder";
4913 else
4914 errstr = "Invalid collection";
4916 if(errstr){
4917 Tcl_SetResult(interp, errstr, TCL_VOLATILE);
4918 return(TCL_ERROR);
4921 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4922 return(TCL_OK);
4924 else if(!strcmp(op, "export")){
4926 * CMD: export
4928 * Returns: success or failure after writing given
4929 * folder to given local file.
4931 * Format:
4932 * 0) Collection Number
4933 * 1) Folder
4934 * 2) Destination file
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;
4945 if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4946 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
4947 if(i == colid)
4948 break;
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")){
4955 snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile);
4957 if(pine_mail_create(NULL, tmp)){
4959 err = NULL; /* reset error condition */
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.
4967 * comments:
4968 * - BUG: what about logins?
4971 if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4972 && (src = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
4973 long retflags = 0;
4975 our_stream = 1;
4976 src = context_open(cp, NULL, folder,
4977 SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE,
4978 &retflags);
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;
4989 snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
4990 mail_fetchfast (src, seq);
4992 ps_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 ps_global->c_client_error);
4999 ps_global->noshow_error = 0;
5001 if(our_stream)
5002 pine_mail_close(src);
5004 else
5005 err = "PEFolder: export: can't open mail folder";
5007 if(!err)
5008 return(TCL_OK);
5010 else
5011 err = "PEFolder: export: can't create destination";
5013 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix"))
5014 err = "PEFolder: export: can't disable driver";
5016 else
5017 err = "PEFolder: export: can't enable driver";
5019 else
5020 err = "PEFolder: export: can't read file name";
5022 else
5023 err = "PEFolder: export: can't read folder name";
5025 else
5026 err = "PEFolder: export: Invalid collection ID";
5028 else
5029 err = "PEFolder:export: Can't read collection ID";
5031 else
5032 err = "PEFolder: export <colid> <folder> <file>";
5034 else if(!strcmp(op, "import")){
5036 * CMD: import
5038 * Returns: success or failure after writing given
5039 * folder to given local file.
5041 * Format:
5042 * 0) source file
5043 * 1) destination collection number
5044 * 2) destination folder
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];
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")){
5059 ps_global->noshow_error = 1; /* don't queue error msg */
5060 err = NULL; /* reset error condition */
5062 /* make sure sfile contains valid mail */
5063 if((src = mail_open(NULL, sfile, 0L)) != NULL){
5065 if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){
5066 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
5067 if(i == colid)
5068 break;
5070 if(cp){
5071 if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
5072 long retflags = 0;
5074 if(context_create(cp, NULL, folder)
5075 && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){
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;
5085 snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
5086 mail_fetchfast (src, seq);
5088 if(!context_append_multiple(cp, dst, folder,
5089 peAppendMsg, (void *) &pkg,
5090 ps_global->mail_stream)){
5091 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5092 ps_global->c_client_error);
5097 pine_mail_close(dst);
5099 else
5100 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5101 ps_global->c_client_error);
5103 else
5104 err = "PEFolder: import: can't read folder name";
5106 else
5107 err = "PEFolder:import: invalid collection id";
5109 else
5110 err = "PEFolder: import: can't read collection id";
5112 mail_close(src);
5115 else
5116 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5117 ps_global->c_client_error);
5119 ps_global->noshow_error = 0;
5121 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err)
5122 err = "PEFolder: import: can't disable driver";
5124 if(!err)
5125 return(TCL_OK);
5127 else
5128 err = "PEFolder: import: can't enable driver";
5130 else
5131 err = "PEFolder: import: can't read file name";
5133 else
5134 err = "PEFolder: import <file> <colid> <folder>";
5136 else {
5137 int i, colid;
5138 char *aes, *colstr;
5139 CONTEXT_S *cp;
5142 * 3 or more arguments, 3rd is the collection ID, rest
5143 * are a folder name
5146 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
5147 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
5148 if(i == colid) break;
5150 else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
5151 if(!strcmp("default", colstr))
5152 cp = default_save_context(ps_global->context_list);
5153 else
5154 cp = NULL;
5156 else
5157 cp = NULL;
5159 if(cp){
5160 if(!strcmp(op, "list")){
5161 int i, fcount, bflags = BFL_NONE;
5163 if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR)
5164 return TCL_ERROR;
5166 if(cp->use & CNTXT_NEWS)
5167 bflags |= BFL_LSUB;
5169 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
5171 pePrepareForAuthException();
5173 build_folder_list(NULL, cp, "*", NULL, bflags);
5175 if((aes = peAuthException()) != NULL){
5176 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5177 reset_context_folders(cp);
5178 return(TCL_ERROR);
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));
5186 p = type;
5187 if(f->isdir){
5188 *p++ = 'D';
5190 if(f->hasnochildren && !f->haschildren)
5191 *p++ = 'E';
5194 if(f->isfolder
5195 || f->nickname
5196 || (cp->use & CNTXT_INCMNG))
5197 *p++ = 'F';
5199 *p = '\0';
5201 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type,
5202 f->nickname ? f->nickname : f->name);
5206 reset_context_folders(cp);
5207 return(TCL_OK);
5209 else if(!strucmp(op, "exists")){
5210 char *folder, *errstr = NULL;
5211 int rv;
5213 if(objc < 4) {
5214 Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE);
5215 return(TCL_ERROR);
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);
5223 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5224 return TCL_ERROR;
5226 ps_global->c_client_error[0] = '\0';
5227 pePrepareForAuthException();
5229 rv = folder_name_exists(cp, folder, NULL);
5231 if(rv & FEX_ERROR){
5232 if((errstr = peAuthException()) == NULL){
5233 if(ps_global->c_client_error[0])
5234 errstr = ps_global->c_client_error;
5235 else
5236 errstr = "Indeterminate Error";
5240 Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE);
5241 return(errstr ? TCL_ERROR : TCL_OK);
5243 else if(!strucmp(op, "fullname")){
5244 char *folder, *fullname;
5246 if(objc < 4) {
5247 Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE);
5248 return(TCL_ERROR);
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);
5256 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5257 return TCL_ERROR;
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
5271 return(TCL_OK);
5273 else if(!strucmp(op, "create")){
5274 char *aes, *folder;
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);
5282 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5283 return TCL_ERROR;
5285 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
5286 pePrepareForAuthException();
5288 if(!context_create(cp, NULL, folder)){
5289 if((aes = peAuthException()) != NULL){
5290 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5292 else{
5293 Tcl_SetResult(interp,
5294 (ps_global->last_error[0])
5295 ? ps_global->last_error
5296 : (ps_global->c_client_error[0])
5297 ? ps_global->c_client_error
5298 : "Unable to create folder",
5299 TCL_VOLATILE);
5302 reset_context_folders(cp);
5303 return(TCL_ERROR);
5306 Tcl_SetResult(interp, "OK", TCL_STATIC);
5307 reset_context_folders(cp);
5308 return(TCL_OK);
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;
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);
5324 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5325 return TCL_ERROR;
5327 /* so we can check for folder's various properties */
5328 build_folder_list(NULL, cp, folder, NULL, BFL_NONE);
5330 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
5332 pePrepareForAuthException();
5334 /* close open folder, then delete */
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);
5343 if(!((cp->use & CNTXT_INCMNG) && fp->name
5344 && check_for_move_mbox(fp->name, NULL, 0, &target))){
5345 target = NULL;
5348 dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?"));
5350 ew = config_containing_inc_fldr(fp);
5351 if(ps_global->restricted)
5352 readonly = 1;
5353 else{
5354 switch(ew){
5355 case Main:
5356 prc = ps_global->prc;
5357 break;
5358 case Post:
5359 prc = ps_global->post_prc;
5360 break;
5361 case None:
5362 break;
5365 readonly = prc ? prc->readonly : 1;
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);
5374 if(cp == ps_global->context_list
5375 && !(cp->dir && cp->dir->ref)
5376 && strucmp(folder, ps_global->inbox_name) == 0){
5377 Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC);
5378 reset_context_folders(cp);
5379 return(TCL_ERROR);
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);
5386 else if((fp->name
5387 && (strm=context_already_open_stream(cp,fp->name,AOS_NONE)))
5389 (target
5390 && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
5391 if(strm == ps_global->mail_stream)
5392 close_opened++;
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;
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);
5407 if(ret){
5408 Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC);
5409 reset_context_folders(cp);
5410 return(TCL_ERROR);
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);
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);
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){
5435 * Close it, NULL the pointer, and let do_broach_folder fixup
5436 * the rest...
5438 pine_mail_actually_close(strm);
5439 if(close_opened){
5440 do_broach_folder(ps_global->inbox_name,
5441 ps_global->context_list,
5442 NULL, DB_INBOXWOCNTXT);
5447 * Use fp->name since "folder" may be a nickname...
5449 if(ps_global->mail_stream
5450 && context_same_stream(cp, fp->name, ps_global->mail_stream))
5451 del_stream = ps_global->mail_stream;
5453 fnamep = fp->name;
5455 if(!context_delete(cp, del_stream, fnamep)){
5456 if((aes = peAuthException()) != NULL){
5457 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5459 else{
5460 Tcl_SetResult(interp,
5461 (ps_global->last_error[0])
5462 ? ps_global->last_error
5463 : (ps_global->c_client_error[0])
5464 ? ps_global->c_client_error
5465 : "Unable to delete folder",
5466 TCL_VOLATILE);
5469 reset_context_folders(cp);
5470 return(TCL_ERROR);
5474 Tcl_SetResult(interp, "OK", TCL_STATIC);
5475 reset_context_folders(cp);
5476 return(TCL_OK);
5479 * must be at least 5 arguments for the next set of commands
5481 else if(objc < 5) {
5482 Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE);
5483 return(TCL_ERROR);
5485 else if(!strucmp(op, "rename")){
5486 char *folder,*newfolder, *aes;
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);
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);
5500 if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR)
5501 return TCL_ERROR;
5503 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
5504 pePrepareForAuthException();
5506 if(!context_rename(cp, NULL, folder, newfolder)){
5507 if((aes = peAuthException()) != NULL){
5508 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5510 else{
5511 Tcl_SetResult(interp,
5512 (ps_global->last_error[0])
5513 ? ps_global->last_error
5514 : (ps_global->c_client_error[0])
5515 ? ps_global->c_client_error
5516 : "Unable to rename folder",
5517 TCL_VOLATILE);
5519 reset_context_folders(cp);
5520 return(TCL_ERROR);
5522 Tcl_SetResult(interp, "OK", TCL_STATIC);
5523 reset_context_folders(cp);
5524 return(TCL_OK);
5527 else
5528 err = "PEFolder: Unrecognized collection ID";
5531 else
5532 err = "No User Context Established";
5535 Tcl_SetResult(interp, err, TCL_VOLATILE);
5536 return(TCL_ERROR);
5541 * PEMailboxCmd - export various bits of mailbox information
5544 PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
5546 char *op, errbuf[256], *err = "Unknown PEMailbox operation";
5548 dprint((5, "PEMailboxCmd"));
5550 if(objc == 1){
5551 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
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;
5559 peED.uid = 0; /* forget cached embedded data */
5562 * CMD: open <context-index> <folder>
5566 if(objc == 2){
5567 Tcl_SetResult(interp, (!sp_dead_stream(ps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE);
5568 return(TCL_OK);
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 = ps_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;
5579 dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder));
5581 return(peCreateStream(interp, cp, folder, FALSE));
5584 err = "open: Unrecognized collection ID";
5586 else
5587 err = "open: Can't read folder";
5589 else
5590 err = "open: Can't get collection ID";
5592 else if(!strcmp(op, "indexformat")){
5594 * CMD: indexformat
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 emtpy string.
5600 * * the second element which is either
5601 * the percentage width or empty string
5603 if(objc == 2)
5604 return(peIndexFormat(interp));
5606 else if(ps_global && ps_global->mail_stream){
5607 if(!strcmp(op, "select")){
5608 return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
5610 else if(!strcmp(op, "search")){
5611 return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
5613 else if(!strucmp(op, "apply")){
5614 return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2]));
5616 else if(!strcmp(op, "expunge")){
5618 * CMD: expunge
5620 * Returns: OK after having removed deleted messages
5622 char *streamstr = NULL;
5623 MAILSTREAM *stream;
5624 MSGNO_S *msgmap;
5626 if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5627 if(!streamstr
5628 || (streamstr && (strcmp(streamstr, "current") == 0))){
5629 stream = ps_global->mail_stream;
5630 msgmap = sp_msgmap(stream);
5632 else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5633 stream = sp_inbox_stream();
5634 msgmap = sp_msgmap(stream);
5636 else return(TCL_ERROR);
5637 ps_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);
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.
5650 if(mn_get_sort(msgmap) == SortSubject2)
5651 refresh_sort(ps_global->mail_stream, msgmap, FALSE);
5653 else
5654 (void) cmd_expunge_work(stream, msgmap, NULL);
5656 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
5657 return(TCL_OK);
5659 else if(!strcmp(op, "trashdeleted")){
5661 * CMD: trashdeleted
5663 * Returns: OK after moving deleted messages to Trash and expunging
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;
5672 if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5673 if(!streamstr
5674 || (streamstr && (strcmp(streamstr, "current") == 0))){
5675 stream = ps_global->mail_stream;
5676 msgmap = sp_msgmap(stream);
5678 else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5679 stream = sp_inbox_stream();
5680 msgmap = sp_msgmap(stream);
5682 else return(TCL_ERROR);
5684 ps_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);
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.
5696 if(mn_get_sort(msgmap) == SortSubject2)
5697 refresh_sort(ps_global->mail_stream, msgmap, FALSE);
5699 else{
5700 if(!(cp = default_save_context(ps_global->context_list)))
5701 cp = ps_global->context_list;
5703 /* copy to trash if we're not in trash */
5704 if(ps_global->VAR_TRASH_FOLDER
5705 && ps_global->VAR_TRASH_FOLDER[0]
5706 && context_allowed(context_apply(tmp, cp, ps_global->VAR_TRASH_FOLDER, sizeof(tmp)))
5707 && !same_stream_and_mailbox(tmp, stream)){
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);
5718 else
5719 set_lflag(stream, msgmap, n, MN_SLCT, 0);
5722 if(tomove && pseudo_selected(stream, msgmap)){
5724 /* save delted to Trash */
5725 n = save(ps_global, stream,
5726 cp, ps_global->VAR_TRASH_FOLDER,
5727 msgmap, SV_FOR_FILT | SV_FIX_DELS);
5729 /* then remove them */
5730 if(n == tomove){
5731 (void) cmd_expunge_work(stream, msgmap, NULL);
5734 restore_selected(msgmap);
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));
5744 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
5745 return(TCL_OK);
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;
5754 * CMD: nextvector
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
5760 * Returns: vector containing next <count> messagenumbers (and optional attributes)
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){
5766 /* set index range for efficiency */
5767 if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
5768 gPeITop = msgno;
5769 gPeICount = count;
5772 if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){
5774 if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0
5775 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){
5776 mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
5778 for(countdown = count; countdown > 0; countdown--){
5779 imapuid_t uid = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno));
5780 int fetched = 0;
5782 if((vObj = Tcl_NewListObj(0, NULL)) != NULL){
5783 Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno));
5784 peAppListF(interp, vObj, "%lu", uid);
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(ps_global, ps_global->mail_stream,
5792 sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
5793 gPeITop, gPeICount, &fetched);
5794 Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1));
5796 else if(!strcmp(s, "statuslist")){
5797 Tcl_Obj *nObj = peMsgStatNameList(interp, ps_global, ps_global->mail_stream,
5798 sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
5799 gPeITop, gPeICount, &fetched);
5800 Tcl_ListObjAppendElement(interp, avObj, nObj);
5802 else if(!strcmp(s, "status")){
5803 long raw;
5804 char stat[3];
5805 MESSAGECACHE *mc;
5807 raw = peSequenceNumber(uid);
5809 if(!((mc = mail_elt(ps_global->mail_stream, raw)) && mc->valid)){
5810 mail_fetch_flags(ps_global->mail_stream,
5811 ulong2string(uid), FT_UID);
5812 mc = mail_elt(ps_global->mail_stream, raw);
5815 stat[0] = mc->deleted ? '1' : '0';
5816 stat[1] = mc->recent ? '1' : '0';
5817 stat[2] = mc->seen ? '1' : '0';
5819 Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3));
5821 else if(!strcmp(s, "indexparts")){
5822 Tcl_Obj *iObj;
5824 if((iObj = Tcl_NewListObj(0, NULL)) != NULL
5825 && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK
5826 && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){
5828 else
5829 return(TCL_ERROR);
5831 else if(!strucmp(s, "indexcolor")){
5832 if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK)
5833 return(TCL_ERROR);
5836 else{
5837 errstr = "nextvector: can't read attributes";
5838 break;
5842 Tcl_ListObjAppendElement(interp, vObj, avObj);
5844 else{
5845 errstr = "nextvector: can't allocate attribute return vector";
5846 break;
5850 else{
5851 errstr = "nextvector: can't allocate new vector";
5852 break;
5855 Tcl_ListObjAppendElement(interp, rvObj, vObj);
5857 for(++msgno; msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream)) && msgline_hidden(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_NONE); msgno++)
5860 if(msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))
5861 break;
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);
5874 else
5875 errstr = "nextvector: can't read attribute list";
5877 else
5878 errstr = "nextvector: can't read count";
5880 else
5881 errstr = "nextvector: can't read message number";
5883 else
5884 errstr = "nextvector: Incorrect number of arguments";
5886 if(errstr)
5887 Tcl_SetResult(interp, errstr, TCL_STATIC);
5889 return(TCL_ERROR);
5891 else if(objc == 2){
5892 if(!strcmp(op, "messagecount")){
5894 * CMD: messagecount
5896 * Returns: count of messsages in open mailbox
5898 Tcl_SetResult(interp,
5899 long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))),
5900 TCL_VOLATILE);
5901 return(TCL_OK);
5903 else if(!strcmp(op, "firstinteresting")){
5905 * CMD: firstinteresting
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?)
5913 Tcl_SetResult(interp,
5914 long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
5915 TCL_VOLATILE);
5916 return(TCL_OK);
5918 else if(!strcmp(op, "selected")){
5920 * CMD: selected
5922 * Returns: count of selected messsages in open mailbox
5925 Tcl_SetResult(interp,
5926 long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)),
5927 TCL_VOLATILE);
5928 return(TCL_OK);
5930 else if(!strcmp(op, "searched")){
5932 * CMD: searched
5934 * Returns: count of searched messsages in open mailbox
5937 Tcl_SetResult(interp,
5938 long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH)),
5939 TCL_VOLATILE);
5940 return(TCL_OK);
5942 else if(!strcmp(op, "mailboxname")){
5944 * CMD: name
5946 * Returns: string representing the name of the
5947 * current mailbox
5949 Tcl_SetResult(interp, ps_global->cur_folder, TCL_VOLATILE);
5950 return(TCL_OK);
5952 else if(!strcmp(op, "close")){
5954 * CMD: close
5956 * Returns: with global mail_stream closed
5958 peDestroyStream(ps_global);
5959 return(TCL_OK);
5961 else if(!strcmp(op, "newmailreset")){
5962 sml_seen();
5963 zero_new_mail_count();
5964 sp_set_mail_box_changed(ps_global->mail_stream, 0);
5965 sp_set_expunge_count(ps_global->mail_stream, 0L);
5966 peMarkInputTime();
5967 return(TCL_OK);
5969 else if(!strcmp(op, "newmailstatmsg")){
5970 long newest, count;
5971 char subject[500], subjtxt[500], from[500], intro[500], *s = "";
5974 * CMD: newmailstatmsg
5976 * ARGS: none
5978 * Returns: text for new mail message
5982 if(sp_mail_box_changed(ps_global->mail_stream)
5983 && (count = sp_mail_since_cmd(ps_global->mail_stream))){
5985 for(newest = ps_global->mail_stream->nmsgs; newest > 1L; newest--)
5986 if(!get_lflag(ps_global->mail_stream, NULL, newest, MN_EXLD))
5987 break;
5989 if(newest){
5990 format_new_mail_msg(NULL, count,
5991 pine_mail_fetchstructure(ps_global->mail_stream,
5992 newest, NULL),
5993 intro, from, subject, subjtxt, sizeof(subject));
5995 snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt);
5999 Tcl_SetResult(interp, s, TCL_VOLATILE);
6000 return(TCL_OK);
6002 else if(!strcmp(op, "savedefault")){
6003 return(peSaveDefault(interp, 0L, 0, NULL));
6005 else if(!strcmp(op, "gotodefault")){
6006 return(peGotoDefault(interp, 0L, NULL));
6008 else if(!strcmp(op, "zoom")){
6009 Tcl_SetResult(interp,
6010 long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
6011 ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT) : 0L),
6012 TCL_VOLATILE);
6013 return(TCL_OK);
6015 else if(!strcmp(op, "focus")){
6016 Tcl_SetResult(interp,
6017 long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
6018 ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH) : 0L),
6019 TCL_VOLATILE);
6020 return(TCL_OK);
6022 else if(!strcmp(op, "first")){
6023 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
6024 long n;
6026 for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
6027 if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
6028 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6029 return(TCL_OK);
6032 unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
6036 Tcl_SetResult(interp, int2string(1), TCL_VOLATILE);
6037 return(TCL_OK);
6039 else if(!strucmp(op, "current")){
6040 long n = 0;
6041 unsigned long u = 0;
6044 * CMD: current
6046 * ARGS:
6048 * Returns: list of current msg {<sequence> <uid>}
6051 if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 0
6052 || ((n = mn_get_cur(sp_msgmap(ps_global->mail_stream))) > 0
6053 && (u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_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);
6058 else
6059 err = "Cannot get current";
6061 else if(!strcmp(op, "last")){
6062 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
6063 long n;
6065 for(n = mn_get_total(sp_msgmap(ps_global->mail_stream)); n > 0L; n--)
6066 if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
6067 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6068 return(TCL_OK);
6071 else{
6072 Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
6073 return(TCL_OK);
6076 Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC);
6077 return(TCL_ERROR);
6079 else if(!strucmp(op, "sortstyles")){
6080 int i;
6082 * CMD: sortstyles
6084 * Returns: list of supported sort styles
6087 for(i = 0; ps_global->sort_types[i] != EndofList; i++)
6088 if(Tcl_ListObjAppendElement(interp,
6089 Tcl_GetObjResult(interp),
6090 Tcl_NewStringObj(sort_name(ps_global->sort_types[i]), -1)) != TCL_OK)
6091 return(TCL_ERROR);
6093 return(TCL_OK);
6095 else if(!strucmp(op, "sort")){
6096 return(peAppendCurrentSort(interp));
6098 else if(!strucmp(op, "state")){
6099 if(!ps_global->mail_stream || sp_dead_stream(ps_global->mail_stream))
6100 Tcl_SetResult(interp, "closed", TCL_STATIC);
6101 else if(ps_global->mail_stream->rdonly && !IS_NEWS(ps_global->mail_stream))
6102 Tcl_SetResult(interp, "readonly", TCL_STATIC);
6103 else
6104 Tcl_SetResult(interp, "ok", TCL_STATIC);
6106 return(TCL_OK);
6108 else if(!strucmp(op, "excludedeleted")){
6109 msgno_exclude_deleted(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL);
6110 return(TCL_OK);
6113 else if(objc == 3){
6114 if(!strcmp(op, "uid")){
6115 long msgno, raw;
6118 * Return uid of given message number
6120 * CMD: uid <msgnumber>
6123 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK)
6124 return(TCL_ERROR); /* conversion problem? */
6126 if((raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno)) > 0L){
6127 raw = mail_uid(ps_global->mail_stream, raw);
6128 Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE);
6129 return(TCL_OK);
6132 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno);
6133 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
6134 return(TCL_ERROR);
6136 else if(!strcmp(op, "newmail")){
6137 int reload, force = UFU_NONE, rv;
6138 time_t now = time(0);
6141 * CMD: newmail
6143 * ARGS: reload -- "1" if we're reloading
6144 * (vs. just checking newmail as a side effect
6145 * of building a new page)
6147 * Returns: count -
6148 * mostrecent -
6150 if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
6151 reload = 0;
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();
6159 /* check for new mail */
6160 new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE);
6162 rv = peNewMailResult(interp);
6164 if(!reload) /* announced */
6165 zero_new_mail_count();
6167 return(rv);
6169 else if(!strcmp(op, "flagcount")){
6170 char *flag;
6171 long count = 0L;
6172 long flags = 0L;
6173 int objlc;
6174 Tcl_Obj **objlv;
6178 * CMD: flagcount
6180 * ARGS: flags -
6182 * Returns: count - number of message thusly flagged
6183 * mostrecent -
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;
6191 if(!strucmp(flag, "undeleted")){
6192 flags |= F_UNDEL;
6194 else if(!strucmp(flag, "seen")){
6195 flags |= F_SEEN;
6197 else if(!strucmp(flag, "unseen")){
6198 flags |= F_UNSEEN;
6200 else if(!strucmp(flag, "flagged")){
6201 flags |= F_FLAG;
6203 else if(!strucmp(flag, "unflagged")){
6204 flags |= F_UNFLAG;
6206 else if(!strucmp(flag, "answered")){
6207 flags |= F_ANS;
6209 else if(!strucmp(flag, "unanswered")){
6210 flags |= F_UNANS;
6212 else if(!strucmp(flag, "recent")){
6213 flags |= F_RECENT;
6215 else if(!strucmp(flag, "unrecent")){
6216 flags |= F_UNRECENT;
6220 if(flags)
6221 count = count_flagged(ps_global->mail_stream, flags);
6224 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6225 return(TCL_OK);
6227 else if(!strcmp(op, "zoom")){
6228 int newstate;
6229 long n, zoomed = 0L;
6232 * CMD: zoom
6234 * Set/clear HID bits of non SLCT messages as requested.
6235 * PEMailbox [first | last | next] are senstive to these flags.
6237 * ARGS: newstate - 1 or 0
6239 * Returns: count of zoomed messages
6242 if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6243 if(newstate > 0){
6244 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)))){
6245 zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SLCT);
6246 zoomed = n;
6249 else{
6250 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
6251 unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
6255 Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6256 return(TCL_OK);
6258 else if(!strcmp(op, "focus")){
6259 int newstate;
6260 long n, zoomed = 0L;
6263 * CMD: focus
6265 * Set/clear HID bits of non MN_SRCH messages as requested.
6266 * PEMailbox [first | last | next] are senstive to MN_HIDE flag
6268 * ARGS: newstate - 1 or 0
6270 * Returns: count of zoomed messages
6273 if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6274 if(newstate > 0){
6275 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH))))
6276 zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SRCH);
6278 zoomed = n;
6280 else{
6281 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
6282 unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
6286 Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6287 return(TCL_OK);
6289 else if(!strcmp(op, "next")){
6290 long msgno;
6293 * CMD: next <msgno>
6295 * ARGS: msgno - message number "next" is relative to
6297 * Returns: previous state
6300 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){
6301 mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
6302 mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
6303 Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
6304 return(TCL_OK);
6307 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6308 return(TCL_ERROR);
6311 else if(objc == 4){
6312 if(!strucmp(op, "sort")){
6313 int i, reversed = 0;
6314 char *sort;
6317 * CMD: sort sortstyle reversed
6319 * Returns: OK with the side-effect of message
6320 * numbers now reflecting the requested
6321 * sort order.
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; ps_global->sort_types[i] != EndofList; i++)
6328 if(strucmp(sort_name(ps_global->sort_types[i]), sort) == 0){
6329 if(sp_unsorted_newmail(ps_global->mail_stream)
6330 || !(ps_global->sort_types[i] == mn_get_sort(sp_msgmap(ps_global->mail_stream))
6331 && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed))
6332 sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
6333 ps_global->sort_types[i],
6334 reversed, 0);
6336 break;
6340 return(peAppendCurrentSort(interp));
6342 else if(!strucmp(op, "selected")){
6343 return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
6345 else if(!strucmp(op, "searched")){
6346 return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
6348 else if(!strcmp(op, "next")){
6349 long msgno, count;
6352 * CMD: next
6354 * ARGS: msgno - message number "next" is relative to
6355 * count - how many to increment it
6357 * Returns: previous state
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(ps_global->mail_stream), msgno);
6363 while(count)
6364 if(count > 0){
6365 mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
6366 count--;
6368 else{
6369 mn_dec_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
6370 count++;
6373 Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
6374 return(TCL_OK);
6377 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6378 return(TCL_ERROR);
6380 else if(!strcmp(op, "x-nextvector")){
6381 long msgno, count;
6384 * CMD: nextvector
6386 * ARGS: msgno - message number "next" is relative to
6387 * count - how many msgno slots to return
6389 * Returns: vector containing next <count> messagenumbers
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(ps_global->mail_stream)))){
6395 mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
6397 while(count--){
6398 long n = mn_get_cur(sp_msgmap(ps_global->mail_stream));
6400 if(peAppListF(interp, Tcl_GetObjResult(interp),
6401 "%l%l", n, mail_uid(ps_global->mail_stream,
6402 mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) != TCL_OK)
6403 return(TCL_ERROR);
6405 mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
6407 if(n == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
6408 break;
6412 return(TCL_OK);
6415 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6416 return(TCL_ERROR);
6418 else if(!strcmp(op, "messagecount")){
6419 char *relative;
6420 long msgno, n, count = 0L;
6423 * CMD: messagecount
6425 * ARGS: [before | after] relative to
6426 * msgno
6428 * Returns: count of messsages before or after given message number
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(ps_global->mail_stream))){
6434 Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC);
6435 return(TCL_ERROR);
6438 if(!strucmp(relative, "before")){
6439 for(n = msgno - 1; n > 0L; n--)
6440 if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
6441 count++;
6443 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6444 return(TCL_OK);
6446 else if(!strucmp(relative, "after")){
6447 for(n = msgno + 1; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
6448 if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
6449 count++;
6451 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6452 return(TCL_OK);
6456 Tcl_SetResult(interp, "can't read range for count", TCL_STATIC);
6457 return(TCL_ERROR);
6459 else if(!strcmp(op, "selectvector")){
6460 long msgno, count;
6463 * CMD: selectvector
6465 * ARGS: msgno - message number "next" is relative to
6466 * count - how many msgno slots to return
6468 * Returns: vector containing next <count> messagenumbers
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(ps_global->mail_stream), msgno);
6475 while(count--){
6476 msgno = mn_get_cur(sp_msgmap(ps_global->mail_stream));
6478 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
6479 if(Tcl_ListObjAppendElement(interp,
6480 Tcl_GetObjResult(interp),
6481 Tcl_NewLongObj((long) mail_uid(ps_global->mail_stream, msgno))) != TCL_OK)
6482 return(TCL_ERROR);
6484 mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
6486 if(msgno == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
6487 break;
6491 return(TCL_OK);
6494 Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC);
6495 return(TCL_ERROR);
6497 else if(!strucmp(op, "current")){
6498 char *which;
6499 long x, n = 0, u = 0;
6502 * CMD: current
6504 * ARGS: (number|uid) <msgno>
6506 * Returns: list of current msg {<sequence> <uid>}
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);
6514 else if(!strucmp(which,"number")){
6515 n = x;
6516 u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
6519 if(n && u){
6520 mn_set_cur(sp_msgmap(ps_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);
6525 else
6526 err = "PEMailbox current: invalid number/uid";
6528 else
6529 err = "PEMailbox current: cannot get number";
6531 else
6532 err = "PEMailbox current: cannot get which";
6535 else
6536 err = "PEMailbox: Too many arguments";
6538 else if(!strucmp(op, "name") || !strcmp(op, "close")){
6539 Tcl_SetResult(interp, "", TCL_STATIC);
6540 return(TCL_OK);
6542 else
6543 snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox",
6544 Tcl_GetStringFromObj(objv[0], NULL), op);
6547 Tcl_SetResult(interp, err, TCL_VOLATILE);
6548 return(TCL_ERROR);
6553 peAppendCurrentSort(Tcl_Interp *interp)
6555 return((Tcl_ListObjAppendElement(interp,
6556 Tcl_GetObjResult(interp),
6557 Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(ps_global->mail_stream))), -1)) == TCL_OK
6558 && Tcl_ListObjAppendElement(interp,
6559 Tcl_GetObjResult(interp),
6560 Tcl_NewStringObj(mn_get_revsort(sp_msgmap(ps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK)
6561 ? TCL_OK : TCL_ERROR);
6566 peAppendDefaultSort(Tcl_Interp *interp)
6568 return((Tcl_ListObjAppendElement(interp,
6569 Tcl_GetObjResult(interp),
6570 Tcl_NewStringObj(sort_name(ps_global->def_sort), -1)) == TCL_OK
6571 && Tcl_ListObjAppendElement(interp,
6572 Tcl_GetObjResult(interp),
6573 Tcl_NewStringObj(ps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK)
6574 ? TCL_OK : TCL_ERROR);
6579 peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
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;
6588 hidden = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L;
6589 mm_search_stream = ps_global->mail_stream;
6590 mm_search_count = 0L;
6592 for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
6593 if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
6594 mc->searched = 0;
6595 mc->spare7 = 1;
6599 * CMD: select
6601 * ARGS: subcmd subcmdargs
6603 * Returns: flip "matchflag" private bit on all or none
6604 * of the messages in the mailbox
6606 if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
6607 if(!strucmp(subcmd, "all")){
6609 * Args: <none>
6611 if(matchflag & MN_SLCT){
6612 if(objc != 1)
6613 return(peSelectError(interp, subcmd));
6615 agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL, 1);
6617 else if(matchflag & MN_SRCH){
6618 for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
6619 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 1);
6622 Tcl_SetResult(interp, "All", TCL_VOLATILE);
6624 else if(!strucmp(subcmd, "none")){
6626 * Args: <none>
6628 n = 0L;
6630 if(matchflag & MN_SLCT){
6631 if(objc != 1)
6632 return(peSelectError(interp, subcmd));
6634 agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), &n, 0);
6636 else if(matchflag & MN_SRCH){
6637 for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
6638 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 0);
6641 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6643 else if(!strucmp(subcmd, "searched")){
6645 * Args: <none>
6647 for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
6648 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
6649 i++;
6650 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 1);
6653 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
6655 else if(!strucmp(subcmd, "unsearched")){
6657 * Args: <none>
6659 for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
6660 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
6661 i++;
6662 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 0);
6665 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
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"));
6675 if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL)))
6676 return(peSelectError(interp, "missing subcommand"));
6678 if(!strucmp(subcmd, "num")){
6679 if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6680 return(i);
6682 else if(!strucmp(subcmd, "date")){
6683 if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6684 return(i);
6686 else if(!strucmp(subcmd, "text")){
6687 if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6688 return(i);
6690 else if(!strucmp(subcmd, "status")){
6691 if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6692 return(i);
6694 else if(!strucmp(subcmd, "compound")){
6695 char *s;
6696 int nSearchList, nSearch;
6697 Tcl_Obj **oSearchList, **oSearch;
6699 /* BUG: should set up one SEARCHPGM to fit criteria and issue single search */
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);
6709 else if(!strucmp(s,"text")){
6710 if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6711 return(n);
6713 else if(!strucmp(s,"status")){
6714 if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6715 return(n);
6717 else
6718 return(peSelectError(interp, "unknown compound search"));
6720 /* logical AND the results */
6721 mm_search_count = 0L;
6722 for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
6723 if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
6724 if(mc->searched && mc->spare7)
6725 mm_search_count++;
6726 else
6727 mc->searched = mc->spare7 = 0;
6730 else
6731 return(peSelectError(interp, "malformed compound search"));
6733 else
6734 return(peSelectError(interp, "malformed compound search"));
6737 else
6738 return(peSelectError(interp, "malformed compound search"));
6740 else
6741 return(peSelectError(interp, "cmd cmdargs"));
6744 * at this point all interesting messages should
6745 * have searched bit lit
6748 if(narrow) /* make sure something was selected */
6749 for(i = 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
6750 if(mail_elt(ps_global->mail_stream,
6751 mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
6752 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag))
6753 break;
6754 else
6755 mm_search_count--;
6758 diff = 0L;
6759 if(mm_search_count){
6761 * loop thru all the messages, adjusting local flag bits
6762 * based on their "searched" bit...
6764 for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
6765 if(narrow){
6766 /* turning OFF selectedness if the "searched" bit isn't lit. */
6767 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
6768 if(!mail_elt(ps_global->mail_stream,
6769 mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
6770 diff--;
6771 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 0);
6772 if(hidden)
6773 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 1);
6775 else if(msgno < mn_get_cur(sp_msgmap(ps_global->mail_stream)))
6776 msgno = i;
6779 else if(mail_elt(ps_global->mail_stream,mn_m2raw(sp_msgmap(ps_global->mail_stream),i))->searched){
6780 /* turn ON selectedness if "searched" bit is lit. */
6781 if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
6782 diff++;
6783 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 1);
6784 if(hidden)
6785 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 0);
6789 /* if we're zoomed and the current message was unselected */
6790 if(narrow && msgno
6791 && get_lflag(ps_global->mail_stream,sp_msgmap(ps_global->mail_stream),mn_get_cur(sp_msgmap(ps_global->mail_stream)),MN_HIDE))
6792 mn_reset_cur(sp_msgmap(ps_global->mail_stream), msgno);
6795 Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE);
6798 return(TCL_OK);
6801 Tcl_SetResult(interp, "Can't read select option", TCL_STATIC);
6802 return(TCL_ERROR);
6806 peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6809 * Args: [broad | narrow] firstnumber lastnumber
6812 long first = 0L, last = 0L, n;
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;
6823 if(first >= 1L && first <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
6824 if(last){
6825 if(last >= 1L && last <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
6826 for(n = first; n <= last; n++)
6827 mm_searched(ps_global->mail_stream,
6828 mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
6830 else
6831 return(peSelectError(interp, "last out of range"));
6833 else{
6834 mm_searched(ps_global->mail_stream,
6835 mn_m2raw(sp_msgmap(ps_global->mail_stream), first));
6838 else
6839 return(peSelectError(interp, "first out of range"));
6841 else
6842 return(peSelectError(interp, "can't read first/last"));
6844 else
6845 return(peSelectError(interp, "num first last"));
6847 return(TCL_OK);
6851 peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6854 * Args: [broad | narrow]
6855 * tense - "on", "since", "before"
6856 * year - 4 digit year
6857 * month - abbreviated month "jan", "feb"...
6858 * day - day number
6861 char *tense, *year, *month, *day, buf[256];
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(ps_global->mail_stream, NULL,
6870 mail_criteria(buf),
6871 SE_NOPREFETCH | SE_FREE);
6873 else
6874 return(peSelectError(interp, "<with valid day>"));
6876 else
6877 return(peSelectError(interp, "<with valid month>"));
6879 else
6880 return(peSelectError(interp, "<with valid year>"));
6882 else
6883 return(peSelectError(interp, "<with valid tense>"));
6885 else
6886 return(peSelectError(interp, "date tense year monthabbrev daynum"));
6888 return(TCL_OK);
6892 peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6895 * Args: [broad | narrow]
6896 * case - in not
6897 * field - to from cc recip partic subj any
6898 * text - free text search string
6900 int not;
6901 char field, *text;
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(ps_global->mail_stream,
6910 sp_msgmap(ps_global->mail_stream),
6911 field, NULL, not, 0, text, NULL, NULL))
6912 /* BUG: plug in "charset" above? */
6913 return(peSelectError(interp, "programmer botch"));
6915 else
6916 return(peSelectError(interp, "<with search string < 1024>"));
6918 else
6919 return(peSelectError(interp, "<with valid field>"));
6921 else
6922 return(peSelectError(interp, "<with valid case>"));
6924 else
6925 return(peSelectError(interp, "text case field text"));
6927 return(TCL_OK);
6931 peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6934 * Args: [broad | narrow]
6935 * case - on not
6936 * status - imp new ans del
6938 int not;
6939 char flag;
6941 if(objc == 2){
6942 if((not = peSelValCase(objv[0])) >= 0){
6943 if((flag = peSelValFlag(objv[1])) != '\0'){
6944 if(agg_flag_select(ps_global->mail_stream, not, flag, NULL))
6945 return(peSelectError(interp, "programmer botch"));
6947 else
6948 return(peSelectError(interp, "<with valid flag>"));
6950 else
6951 return(peSelectError(interp, "<with valid case>"));
6953 else
6954 return(peSelectError(interp, "status focus case flag"));
6956 return(TCL_OK);
6959 char *
6960 peSelValTense(Tcl_Obj *objp)
6962 char *tense, **pp;
6964 if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){
6965 static char *tenses[] = {"on", "since", "before", NULL};
6967 for(pp = tenses; *pp; pp++)
6968 if(!strucmp(*pp, tense))
6969 return(tense);
6972 return(NULL);
6976 char *
6977 peSelValYear(Tcl_Obj *objp)
6979 char *year;
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);
6991 char *
6992 peSelValMonth(Tcl_Obj *objp)
6994 char *month, **pp;
6995 static char *mons[] = {"jan","feb","mar","apr",
6996 "may","jun","jul","aug",
6997 "sep","oct","nov","dec", NULL};
6999 if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3)
7000 for(pp = mons; *pp; pp++)
7001 if(!strucmp(month, *pp))
7002 return(*pp);
7004 return(NULL);
7008 char *
7009 peSelValDay(Tcl_Obj *objp)
7011 char *day;
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);
7024 peSelValCase(Tcl_Obj *objp)
7026 char *not;
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);
7035 return(-1);
7040 peSelValField(Tcl_Obj *objp)
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}};
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);
7062 return(0);
7067 peSelValFlag(Tcl_Obj *objp)
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}};
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);
7085 return(0);
7089 peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
7091 int rv = 0;
7092 long i, n;
7093 char *range;
7096 * CMD: searched [before | after] #
7098 * Returns: 1 if criteria is true, 0 otherwise
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(ps_global->mail_stream)); i++)
7105 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
7106 rv = 1;
7107 break;
7110 Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7111 return(TCL_OK);
7113 else if(!strucmp(range, "after")){
7114 for(i = n + 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
7115 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
7116 rv = 1;
7117 break;
7120 Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7121 return(TCL_OK);
7125 Tcl_SetResult(interp, "searched test failed", TCL_STATIC);
7126 return(TCL_ERROR);
7131 peSelectError(Tcl_Interp *interp, char *usage)
7133 char buf[256];
7135 snprintf(buf, sizeof(buf), "should be select %.128s", usage);
7136 Tcl_SetResult(interp, buf, TCL_VOLATILE);
7137 return(TCL_ERROR);
7142 peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv)
7144 char *subcmd;
7145 long n;
7147 if(!(n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT))){
7148 Tcl_SetResult(interp, "No messages selected", TCL_STATIC);
7149 return(TCL_ERROR);
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(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL);
7156 Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7157 return(TCL_OK);
7159 else if(!strucmp(subcmd, "undelete")){
7160 (void) cmd_undelete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT);
7161 Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7162 return(TCL_OK);
7165 else if(objc == 2){
7166 if(!strucmp(subcmd, "count")){
7168 * Args: flag
7170 char *flagname;
7171 long n, rawno, count = 0;
7173 if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
7174 for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){
7175 rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
7176 if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)
7177 && peIsFlagged(ps_global->mail_stream,
7178 mail_uid(ps_global->mail_stream, rawno),
7179 flagname)){
7180 count++;
7185 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
7186 return(TCL_OK);
7189 else if(objc == 3){
7190 if(!strucmp(subcmd, "flag")){
7192 * Args: case - on not
7193 * flag - imp new ans del
7195 char flag, *result;
7196 int not;
7197 long flagged;
7199 if((not = peSelValCase(objv[1])) >= 0){
7200 if((flag = peSelValFlag(objv[2])) != '\0'){
7201 result = peApplyFlag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), flag, not, &flagged);
7202 if(!result){
7203 Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE);
7204 return(TCL_OK);
7206 else
7207 return(peApplyError(interp, result));
7209 else
7210 return(peApplyError(interp, "invalid flag"));
7212 else
7213 return(peApplyError(interp, "invalid case"));
7215 else if(!strucmp(subcmd, "save")){
7217 * Args: colid -
7218 * folder - imp new ans del
7221 int colid, flgs = 0, i;
7222 char *folder, *err;
7223 CONTEXT_S *cp;
7225 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7227 for(i = 0, cp = ps_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(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
7232 if(!READONLY_FOLDER(ps_global->mail_stream)
7233 && F_OFF(F_SAVE_WONT_DELETE, ps_global))
7234 flgs |= SV_DELETE;
7236 if(colid == 0 && !strucmp(folder, "inbox"))
7237 flgs |= SV_INBOXWOCNTXT;
7239 i = save(ps_global, ps_global->mail_stream,
7240 cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
7242 err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem saving";
7244 restore_selected(sp_msgmap(ps_global->mail_stream));
7245 if(err)
7246 return(peApplyError(interp, err));
7248 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7249 return(TCL_OK);
7251 else
7252 return(peApplyError(interp, "can't select"));
7254 else
7255 return(peApplyError(interp, "no folder name"));
7258 return(peApplyError(interp, "bad colid"));
7260 else
7261 return(peApplyError(interp, "invalid case"));
7263 else if(!strucmp(subcmd, "copy")){
7265 * Args: colid -
7266 * folder - imp new ans del
7269 int colid, flgs = 0, i;
7270 char *folder, *err;
7271 CONTEXT_S *cp;
7273 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7275 for(i = 0, cp = ps_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(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
7280 if(colid == 0 && !strucmp(folder, "inbox"))
7281 flgs |= SV_INBOXWOCNTXT;
7283 i = save(ps_global, ps_global->mail_stream,
7284 cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
7286 err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem copying";
7288 restore_selected(sp_msgmap(ps_global->mail_stream));
7289 if(err)
7290 return(peApplyError(interp, err));
7292 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7293 return(TCL_OK);
7295 else
7296 return(peApplyError(interp, "can't select"));
7298 else
7299 return(peApplyError(interp, "no folder name"));
7302 return(peApplyError(interp, "bad colid"));
7304 else
7305 return(peApplyError(interp, "invalid case"));
7307 else if(!strucmp(subcmd, "move")){
7309 * Args: colid -
7310 * folder - imp new ans del
7313 int colid, flgs = 0, i;
7314 char *folder, *err;
7315 CONTEXT_S *cp;
7317 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7319 for(i = 0, cp = ps_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(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
7324 flgs = SV_DELETE;
7326 if(colid == 0 && !strucmp(folder, "inbox"))
7327 flgs |= SV_INBOXWOCNTXT;
7329 i = save(ps_global, ps_global->mail_stream,
7330 cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
7332 err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem moving";
7334 restore_selected(sp_msgmap(ps_global->mail_stream));
7335 if(err)
7336 return(peApplyError(interp, err));
7338 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7339 return(TCL_OK);
7341 else
7342 return(peApplyError(interp, "can't select"));
7344 else
7345 return(peApplyError(interp, "no folder name"));
7348 return(peApplyError(interp, "bad colid"));
7350 else
7351 return(peApplyError(interp, "invalid case"));
7353 else if(!strucmp(subcmd, "spam")){
7355 * Args: spamaddr -
7356 * spamsubj -
7358 char *spamaddr, *spamsubj = NULL;
7359 long n, rawno;
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(ps_global->mail_stream)); n++){
7364 rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
7365 if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)){
7366 char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
7368 if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){
7369 Tcl_SetResult(interp, rs, TCL_VOLATILE);
7370 return(TCL_ERROR);
7376 Tcl_SetResult(interp, "OK", TCL_VOLATILE);
7377 return(TCL_OK);
7382 return(peApplyError(interp, "unknown option"));
7386 char *
7387 peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged)
7389 char *seq, *flagstr;
7390 long flags, flagid;
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;
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);
7424 restore_selected(msgmap);
7425 return(NULL);
7427 else
7428 return("can't select");
7433 peApplyError(Tcl_Interp *interp, char *usage)
7435 char buf[256];
7437 snprintf(buf, sizeof(buf), "apply error: %.128s", usage);
7438 Tcl_SetResult(interp, buf, TCL_VOLATILE);
7439 return(TCL_ERROR);
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
7449 peIndexFormat(Tcl_Interp *interp)
7451 INDEX_COL_S *cdesc = NULL;
7452 char *name, wbuf[4], *dname;
7454 for(cdesc = ps_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;
7467 case iMessNo:
7468 name = "Number";
7469 break;
7471 case iPrio:
7472 case iPrioAlpha:
7473 case iPrioBang:
7474 name = "Priority";
7475 break;
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;
7497 case iCurDay: case iCurDay2Digit:
7498 case iCurDayOfWeek: case iCurDayOfWeekAbb:
7499 name = "Day";
7500 break;
7502 case iCurMon: case iCurMon2Digit:
7503 case iCurMonLong: case iCurMonAbb:
7504 name= "Month";
7505 break;
7507 case iTime24: case iTime12: case iTimezone:
7508 case iCurPrefTime:
7509 name = "Time";
7510 break;
7512 case iDay2Digit: case iDayOfWeek: case iDayOfWeekAbb:
7513 name = "Day";
7514 break;
7516 case iMonAbb: case iMon2Digit:
7517 name = "Month";
7518 break;
7520 case iYear: case iYear2Digit:
7521 case iCurYear: case iCurYear2Digit:
7522 name = "Year";
7523 break;
7525 case iScore :
7526 name = "Score";
7527 break;
7529 case iFromTo:
7530 case iFromToNotNews:
7531 case iFrom:
7532 name = "From";
7533 break;
7535 case iTo:
7536 case iToAndNews :
7537 name = "To";
7538 break;
7540 case iCc:
7541 name = "Cc";
7542 break;
7544 case iRecips:
7545 name = "Recipients";
7546 break;
7548 case iSender:
7549 name = "Sender";
7550 break;
7552 case iSize :
7553 case iSizeComma :
7554 case iSizeNarrow :
7555 case iDescripSize:
7556 case iKSize :
7557 name = "Size";
7558 break;
7560 case iAtt:
7561 name = "Attachments";
7562 break;
7564 case iAddress :
7565 name = "Address";
7566 break;
7568 case iMailbox :
7569 name = "Mailbox";
7570 break;
7572 case iSubject :
7573 case iSubjKey :
7574 case iSubjKeyInit :
7575 case iSubjectText :
7576 case iSubjKeyText :
7577 case iSubjKeyInitText :
7578 name = "Subject";
7579 break;
7581 case iNews:
7582 case iNewsAndTo :
7583 name = "News";
7584 break;
7586 case iNewsAndRecips:
7587 name = "News/Recip";
7588 break;
7590 case iRecipsAndNews:
7591 name = "Recip/News";
7592 break;
7594 default :
7595 name = "";
7596 break;
7599 if(cdesc->width > 0){
7600 int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH);
7602 snprintf(wbuf, sizeof(wbuf), "%d%%", p);
7604 else
7605 wbuf[0] = '\0';
7607 if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK)
7608 return(TCL_ERROR);
7611 return(TCL_OK);
7616 peNewMailResult(Tcl_Interp *interp)
7618 unsigned long n, uid;
7620 if(sp_mail_box_changed(ps_global->mail_stream)){
7621 if((n = sp_mail_since_cmd(ps_global->mail_stream)) != 0L){
7622 /* first element is count of new messages */
7623 if(Tcl_ListObjAppendElement(interp,
7624 Tcl_GetObjResult(interp),
7625 Tcl_NewLongObj(n)) != TCL_OK)
7626 return(TCL_ERROR);
7628 /* second element is UID of most recent message */
7629 for(uid = ps_global->mail_stream->nmsgs; uid > 1L; uid--)
7630 if(!get_lflag(ps_global->mail_stream, NULL, uid, MN_EXLD))
7631 break;
7633 if(!uid){
7634 Tcl_ResetResult(interp);
7635 Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7636 return(TCL_ERROR);
7639 uid = mail_uid(ps_global->mail_stream, uid);
7641 if(Tcl_ListObjAppendElement(interp,
7642 Tcl_GetObjResult(interp),
7643 Tcl_NewLongObj(uid)) != TCL_OK)
7644 return(TCL_ERROR);
7646 else {
7647 if(Tcl_ListObjAppendElement(interp,
7648 Tcl_GetObjResult(interp),
7649 Tcl_NewIntObj(0)) != TCL_OK)
7650 return(TCL_ERROR);
7652 /* zero is UID of new message */
7653 if(Tcl_ListObjAppendElement(interp,
7654 Tcl_GetObjResult(interp),
7655 Tcl_NewIntObj(0)) != TCL_OK)
7656 return(TCL_ERROR);
7659 /* third element is expunge count */
7660 if(Tcl_ListObjAppendElement(interp,
7661 Tcl_GetObjResult(interp),
7662 Tcl_NewLongObj(sp_expunge_count(ps_global->mail_stream)
7663 ? sp_expunge_count(ps_global->mail_stream)
7664 : 0L)) != TCL_OK)
7665 return(TCL_ERROR);
7668 else
7669 Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7671 return(TCL_OK);
7675 /* * * * * * * * Start of Per-Thread/SubThread access functions * * * * * * * */
7679 * PEThreadCmd - access/manipulate various pieces of thread state
7682 PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7684 char *err, errbuf[256], *cmd, *op;
7685 long uidl;
7686 imapuid_t uid;
7688 dprint((2, "PEThreadCmd"));
7690 snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7691 Tcl_GetStringFromObj(objv[0], NULL));
7693 if(!(ps_global && ps_global->mail_stream)){
7694 snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7695 Tcl_GetStringFromObj(objv[0], NULL));
7697 else if(objc < 2){
7698 Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?");
7700 else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7701 return(TCL_ERROR); /* conversion problem? */
7703 else if(!peSequenceNumber(uidl)){
7704 snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7705 Tcl_GetStringFromObj(objv[0], NULL), uidl);
7707 else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7708 uid = uidl;
7709 if(objc == 3){
7710 if(!strucmp(cmd,"info")){
7711 #define WP_MAX_THRD_PREFIX 256
7712 long raw;
7713 PINETHRD_S *pthrd;
7714 char tstr[WP_MAX_THRD_PREFIX];
7716 if((raw = peSequenceNumber(uid)) != 0L){
7718 * translate PINETHRD_S data into
7720 if((pthrd = msgno_thread_info(ps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){
7722 tstr[0] = '\0';
7723 /* BUG: build tstr form pthrd */
7726 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7727 Tcl_NewStringObj(tstr, -1));
7730 else
7731 Tcl_SetResult(interp, "0", TCL_STATIC);
7733 return(TCL_OK);
7737 else if(objc == 5){
7738 if(!strucmp(cmd,"flag")){
7739 if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
7740 if(!strucmp(op,"deleted")){
7741 int value;
7743 if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){
7744 long n;
7745 PINETHRD_S *pthrd;
7746 char *flag;
7748 while(1){
7749 if(!(n = peSequenceNumber(uid))){
7750 Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC);
7751 return(TCL_ERROR);
7754 flag = cpystr("\\DELETED");
7755 mail_flag(ps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L));
7756 fs_give((void **) &flag);
7758 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7759 Tcl_NewStringObj(ulong2string(uid), -1));
7761 if(++n <= ps_global->mail_stream->nmsgs){
7762 uid = mail_uid(ps_global->mail_stream, n);
7764 else
7765 break;
7767 if((pthrd = msgno_thread_info(ps_global->mail_stream, n, NULL,THD_TOP)) != NULL){
7769 else
7770 break;
7779 Tcl_SetResult(interp, err, TCL_STATIC);
7780 return(TCL_ERROR);
7785 /* * * * * * * * Start of Per-Message access functions * * * * * * * */
7789 static struct _message_cmds {
7790 char *cmd;
7791 int hcount;
7792 struct {
7793 int argcount;
7794 int (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
7795 } h[3];
7796 } message_cmds[] = {
7797 {"size", 1, {{3, peMessageSize}}},
7798 {"date", 2, {{3, peMessageDate}, {4, peMessageDate}}},
7799 {"subject", 1, {{3, peMessageSubject}}},
7800 {"fromaddr", 1, {{3, peMessageFromAddr}}},
7801 {"toaddr", 1, {{3, peMessageToAddr}}},
7802 {"ccaddr", 1, {{3, peMessageCcAddr}}},
7803 {"status", 1, {{3, peMessageStatus}}},
7804 {"statusbits", 1, {{3, peMessageStatusBits}}},
7805 {"charset", 1, {{3, peMessageCharset}}},
7806 {"number", 1, {{3, peMsgnoFromUID}}},
7807 {"envelope", 0},
7808 {"rawenvelope", 0},
7809 {"text", 1, {{3, peMessageText}}},
7810 {"header", 1, {{3, peMessageHeader}}},
7811 {"attachments", 1, {{3, peMessageAttachments}}},
7812 {"body", 3, {{3, peMessageBody}, {4, peMessageBody}}},
7813 {"cid", 1, {{4, peMessagePartFromCID}}},
7814 {"flag", 2, {{4, peGetFlag}, {5, peSetFlag}}},
7815 {"replyheaders", 2, {{3, peReplyHeaders},{4, peReplyHeaders}}},
7816 {"replytext", 2, {{4, peReplyText}, {5, peReplyText}}},
7817 {"forwardheaders", 2, {{3, peForwardHeaders}, {4, peForwardHeaders}}},
7818 {"forwardtext", 2, {{3, peForwardText}, {4, peForwardText}}},
7819 {"rawbody", 0},
7820 {"select", 2, {{3, peMsgSelect}, {4, peMsgSelect}}},
7821 {"detach", 1, {{5, peDetach}}},
7822 {"attachinfo", 1, {{4, peAttachInfo}}},
7823 {"savedefault", 1, {{3, peSaveDefault}}},
7824 {"save", 1, {{5, peSave}}},
7825 {"copy", 1, {{5, peCopy}}},
7826 {"move", 1, {{5, peMove}}},
7827 {"takeaddr", 1, {{3, peTakeaddr}}},
7828 {"takefrom", 1, {{3, peTakeFrom}}},
7829 {"replyquote", 1, {{3, peReplyQuote}}},
7830 {"bounce", 2, {{4, peMessageBounce},{5, peMessageBounce}}},
7831 {"spam", 1, {{5, peMessageSpamNotice}}},
7832 {"needpasswd", 1, {{3, peMessageNeedPassphrase}}},
7833 {NULL, 0}
7840 * PEMessageCmd - export various bits of message information
7842 * NOTE: all exported commands are of the form:
7844 * PEMessage <uid> <cmd> <args>
7847 PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7849 char *err, errbuf[256], *cmd;
7850 int i, j;
7851 long uidl;
7852 imapuid_t uid;
7854 dprint((5, "PEMessageCmd"));
7856 snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7857 Tcl_GetStringFromObj(objv[0], NULL));
7859 if(!(ps_global && ps_global->mail_stream)){
7860 snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7861 Tcl_GetStringFromObj(objv[0], NULL));
7863 else if(objc < 3){
7864 Tcl_WrongNumArgs(interp, 0, objv, "PEMessage <uid> cmd ?args?");
7866 else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7867 return(TCL_ERROR); /* conversion problem? */
7869 else if(!peMessageNumber(uidl)){
7870 snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7871 Tcl_GetStringFromObj(objv[0], NULL), uidl);
7873 else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7874 uid = uidl;
7875 for(i = 0; message_cmds[i].cmd; i++)
7876 if(!strcmp(cmd, message_cmds[i].cmd)){
7877 for(j = 0; j < message_cmds[i].hcount; j++)
7878 if(message_cmds[i].h[j].argcount == objc)
7879 return((*message_cmds[i].h[j].f)(interp, uid, objc - 3,
7880 &((Tcl_Obj **)objv)[3]));
7882 snprintf(err = errbuf, sizeof(errbuf),
7883 "PEMessage: %s: mismatched argument count", cmd);
7884 break;
7888 Tcl_SetResult(interp, err, TCL_STATIC);
7889 return(TCL_ERROR);
7894 * return the uid's ordinal number within the CURRENT SORT
7896 long
7897 peMessageNumber(imapuid_t uid)
7899 return(mn_raw2m(sp_msgmap(ps_global->mail_stream), peSequenceNumber(uid)));
7903 * return the uid's RAW message number (for c-client reference, primarily)
7905 long
7906 peSequenceNumber(imapuid_t uid)
7908 return(mail_msgno(ps_global->mail_stream, uid));
7913 peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7915 long raw;
7917 if((raw = peSequenceNumber(uid))
7918 && pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL)){
7919 Tcl_SetResult(interp,
7920 long2string(mail_elt(ps_global->mail_stream,
7921 raw)->rfc822_size),
7922 TCL_VOLATILE);
7924 else
7925 Tcl_SetResult(interp, "0", TCL_STATIC);
7927 return(TCL_OK);
7932 peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7934 char *cmd;
7935 long raw;
7936 ENVELOPE *env;
7937 MESSAGECACHE mc;
7939 if((raw = peSequenceNumber(uid))
7940 && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
7941 if(objc == 1 && objv[0]){
7942 if(mail_parse_date(&mc, env->date)){
7943 if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
7944 if(!strucmp(cmd,"day")){
7945 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day);
7946 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
7947 return(TCL_OK);
7949 else if(!strucmp(cmd,"month")){
7950 Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE);
7951 return(TCL_OK);
7953 else if(!strucmp(cmd,"year")){
7954 Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE);
7955 return(TCL_OK);
7957 else{
7958 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd);
7959 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
7962 else
7963 Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC);
7965 else
7966 Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC);
7968 else{
7969 Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE);
7970 return(TCL_OK);
7973 else
7974 Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC);
7976 return(TCL_ERROR);
7981 peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7983 return(peMessageField(interp, uid, "from"));
7988 peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7990 return(peMessageField(interp, uid, "to"));
7995 peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7997 return(peMessageField(interp, uid, "cc"));
8002 peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8004 return(peMessageField(interp, uid, "subject"));
8009 peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field)
8011 long raw;
8012 char *s = "";
8013 ENVELOPE *env;
8015 if((raw = peSequenceNumber(uid))
8016 && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
8017 if(!strucmp(field, "from")){
8018 if(env->from && env->from->mailbox)
8019 snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox,
8020 (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : "");
8022 else if(!strucmp(field, "to")){
8023 if(env->to && env->to->mailbox)
8024 snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox,
8025 (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : "");
8027 else if(!strucmp(field, "cc")){
8028 if(env->cc && env->cc->mailbox)
8029 snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox,
8030 (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : "");
8032 else if(!strucmp(field, "subject")){
8033 if(env->subject)
8034 snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject);
8036 else{
8037 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field);
8038 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
8039 return(TCL_ERROR);
8042 Tcl_SetResult(interp, s, TCL_VOLATILE);
8043 return(TCL_OK);
8046 Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC);
8047 return(TCL_ERROR);
8052 peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8054 long raw;
8055 MESSAGECACHE *mc;
8057 if((raw = peSequenceNumber(uid)) != 0L){
8058 if(!((mc = mail_elt(ps_global->mail_stream, raw))
8059 && mc->valid)){
8060 mail_fetch_flags(ps_global->mail_stream,
8061 ulong2string(uid), FT_UID);
8062 mc = mail_elt(ps_global->mail_stream, raw);
8065 if (mc->deleted)
8066 Tcl_ListObjAppendElement(interp,
8067 Tcl_GetObjResult(interp),
8068 Tcl_NewStringObj("Deleted", -1));
8070 if (mc->answered)
8071 Tcl_ListObjAppendElement(interp,
8072 Tcl_GetObjResult(interp),
8073 Tcl_NewStringObj("Answered", -1));
8075 if (!mc->seen)
8076 Tcl_ListObjAppendElement(interp,
8077 Tcl_GetObjResult(interp),
8078 Tcl_NewStringObj("New", -1));
8080 if (mc->flagged)
8081 Tcl_ListObjAppendElement(interp,
8082 Tcl_GetObjResult(interp),
8083 Tcl_NewStringObj("Important", -1));
8086 return(TCL_OK);
8091 peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8093 /* everthing coming out of pith better be utf-8 */
8094 Tcl_SetResult(interp, "UTF-8", TCL_STATIC);
8095 return(TCL_OK);
8100 peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8102 #ifdef SMIME
8103 return((ps_global && ps_global->smime && ps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR);
8104 #else
8105 return(TCL_ERROR);
8106 #endif /* SMIME */
8111 peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8113 Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE);
8114 return(TCL_OK);
8119 * peInterpWritec - collect filtered output, appending to the
8120 * command's result list on each EOL
8123 peInterpWritec(int c)
8125 unsigned char ch = (unsigned char) (0xff & c);
8127 if(ch == '\n')
8128 return(peInterpFlush() == TCL_OK);
8129 else
8130 so_writec(ch, peED.store);
8132 return(1);
8137 * peInterpFlush - write accumulated line to result object mapping
8138 * embedded data into exportable tcl list members
8142 peInterpFlush(void)
8144 char *line, *p, *tp, *tp2, col1[32], col2[32];
8145 Tcl_Obj *lobjp, *objColor, *objPair;
8147 line = (char *) so_text(peED.store);
8149 if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){
8150 if((p = strindex(line, TAG_EMBED)) != NULL){
8152 *p = '\0';
8154 if(p - line)
8155 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8157 switch(*++p){
8158 case TAG_HANDLE :
8160 int i, n;
8161 HANDLE_S *h;
8164 for(n = 0, i = *++p; i > 0; i--)
8165 n = (n * 10) + (*++p - '0');
8167 line = ++p; /* prepare for next section of line */
8169 if(!peED.inhandle){
8170 peED.inhandle = 1;
8172 if((h = get_handle(peED.handles, n)) != NULL)
8173 switch(h->type){
8174 case IMG :
8176 Tcl_Obj *llObj, *rObj;
8178 llObj = Tcl_NewListObj(0, NULL);
8179 Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1));
8181 rObj = Tcl_NewListObj(0, NULL);
8182 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1));
8183 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1));
8185 Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8187 Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8188 peED.inhandle = 0;
8191 break;
8193 case URL :
8195 Tcl_Obj *llObj, *rObj;
8197 llObj = Tcl_NewListObj(0, NULL);
8198 Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1));
8200 rObj = Tcl_NewListObj(0, NULL);
8201 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1));
8202 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1));
8204 Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8206 Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8209 break;
8211 case Attach :
8213 Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj;
8215 alObj = Tcl_NewListObj(0, NULL);
8216 Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1));
8218 peGetMimeTyping(mail_body(ps_global->mail_stream,
8219 peSequenceNumber(peED.uid),
8220 (unsigned char *) h->h.attach->number),
8221 &tObj, &stObj, &fnObj, &eObj);
8224 rObj = Tcl_NewListObj(0, NULL);
8225 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid));
8226 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1));
8227 Tcl_ListObjAppendElement(peED.interp, rObj, tObj);
8228 Tcl_ListObjAppendElement(peED.interp, rObj, stObj);
8229 Tcl_ListObjAppendElement(peED.interp, rObj, fnObj);
8230 Tcl_ListObjAppendElement(peED.interp, rObj, eObj);
8232 Tcl_ListObjAppendElement(peED.interp, alObj, rObj);
8234 Tcl_ListObjAppendElement(peED.interp, lobjp, alObj);
8237 break;
8239 default :
8240 break;
8245 break;
8247 case TAG_FGCOLOR :
8248 if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){
8249 /* look ahead */
8250 if(p[11] == TAG_EMBED
8251 && p[12] == TAG_BGCOLOR
8252 && (tp2 = peColorStr(p + 13, col2))){
8253 objColor = Tcl_NewListObj(0, NULL);
8254 objPair = Tcl_NewListObj(0, NULL);
8255 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8256 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8257 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8258 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8259 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8260 strcpy(peED.color.bg, tp2);
8261 p += 13;
8263 else if(strcmp(peED.color.bg, peED.color.bgdef)){
8264 objColor = Tcl_NewListObj(0, NULL);
8265 objPair = Tcl_NewListObj(0, NULL);
8266 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8267 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8268 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1));
8269 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8270 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8271 strcpy(peED.color.bg, peED.color.bgdef);
8273 else
8274 peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp);
8276 strcpy(peED.color.fg, tp);
8279 line = p + 11;
8280 break;
8282 case TAG_BGCOLOR :
8283 if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){
8284 /* look ahead */
8285 if(p[11] == TAG_EMBED
8286 && p[12] == TAG_FGCOLOR
8287 && (tp2 = peColorStr(p + 13, col2))){
8288 objColor = Tcl_NewListObj(0, NULL);
8289 objPair = Tcl_NewListObj(0, NULL);
8290 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8291 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8292 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8293 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8294 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8295 strcpy(peED.color.fg, tp2);
8296 p += 13;
8298 else if(strcmp(peED.color.fg, peED.color.fgdef)){
8299 objColor = Tcl_NewListObj(0, NULL);
8300 objPair = Tcl_NewListObj(0, NULL);
8301 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8302 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1));
8303 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8304 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8305 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8306 strcpy(peED.color.fg, peED.color.fgdef);
8308 else
8309 peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp);
8311 strcpy(peED.color.bg, tp);
8314 line = p + 11;
8315 break;
8317 case TAG_ITALICON :
8318 peAppListF(peED.interp, lobjp, "%s%s", "italic", "on");
8319 line = p + 1;
8320 break;
8322 case TAG_ITALICOFF :
8323 peAppListF(peED.interp, lobjp, "%s%s", "italic", "off");
8324 line = p + 1;
8325 break;
8327 case TAG_BOLDON :
8328 peAppListF(peED.interp, lobjp, "%s%s", "bold", "on");
8329 line = p + 1;
8330 break;
8332 case TAG_BOLDOFF :
8333 peAppListF(peED.interp, lobjp, "%s%s", "bold", "off");
8334 line = p + 1;
8335 break;
8337 case TAG_ULINEON :
8338 peAppListF(peED.interp, lobjp, "%s%s", "underline", "on");
8339 line = p + 1;
8340 break;
8342 case TAG_ULINEOFF :
8343 peAppListF(peED.interp, lobjp, "%s%s", "underline", "off");
8344 line = p + 1;
8345 break;
8347 case TAG_STRIKEON :
8348 peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on");
8349 line = p + 1;
8350 break;
8352 case TAG_STRIKEOFF :
8353 peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off");
8354 line = p + 1;
8355 break;
8357 case TAG_BIGON :
8358 peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on");
8359 line = p + 1;
8360 break;
8362 case TAG_BIGOFF :
8363 peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off");
8364 line = p + 1;
8365 break;
8367 case TAG_SMALLON :
8368 peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on");
8369 line = p + 1;
8370 break;
8372 case TAG_SMALLOFF :
8373 peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off");
8374 line = p + 1;
8375 break;
8377 case TAG_INVOFF :
8378 case TAG_HANDLEOFF :
8379 if(peED.inhandle){
8380 peAppListF(peED.interp, lobjp, "%s%s", "urlend", "");
8381 peED.inhandle = 0;
8383 /* fall thru and advance "line" */
8385 default :
8386 line = p + 1;
8387 break;
8391 while((p = strindex(line, TAG_EMBED)) != NULL);
8393 if(*line)
8394 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8396 else
8397 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8399 else
8400 peAppListF(peED.interp, lobjp, "%s%s", "t", "");
8402 if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){
8403 so_truncate(peED.store, 0L);
8404 return(TCL_OK);
8407 return(TCL_ERROR);
8413 * peInterpWritec - collect filtered output, appending to the
8414 * command's result list on each EOL
8417 peNullWritec(int c)
8419 return(1);
8423 char *
8424 peColorStr(char *s, char *b)
8426 int i, j, color;
8428 i = 0;
8429 b[0] = '\0';
8430 while(1){
8431 color = 0;
8432 for(j = 0; j < 3; j++, s++)
8433 if(isdigit((unsigned char) *s))
8434 color = (color * 10) + (*s - '0');
8436 s++; /* advance past ',' */
8437 if(color < 256)
8438 sprintf(b + strlen(b), "%2.2x", color);
8439 else
8440 break;
8442 if(++i == 3)
8443 return(b);
8447 return(NULL);
8452 * returns a list of elements
8455 peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8457 MESSAGECACHE *mc;
8458 HEADER_S h;
8459 int flags, rv = TCL_OK;
8460 long raw;
8461 #if 0
8462 char *color;
8463 #endif
8466 * ONLY full header mode (raw) output should get written to the
8467 * writec function we pass format_header. If there's something
8468 * in the store after formatting ,we'll write it to the Tcl result
8469 * then, not as its accumulated
8471 peED.interp = interp;
8472 peED.obj = Tcl_NewStringObj("", -1);
8474 if(peED.store)
8475 so_seek(peED.store, 0L, 0);
8476 else
8477 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8479 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8481 #if 0
8482 peED.color.fg[0] = '\0';
8483 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8484 peInterpWritec(TAG_EMBED);
8485 peInterpWritec(TAG_FGCOLOR);
8486 gf_puts(color, peInterpWritec);
8487 strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
8490 peED.color.bg[0] = '\0';
8491 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8492 peInterpWritec(TAG_EMBED);
8493 peInterpWritec(TAG_BGCOLOR);
8494 gf_puts(color, peInterpWritec);
8495 strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
8498 peInterpFlush();
8499 #endif
8501 raw = peSequenceNumber(uid);
8502 if(peED.uid != uid){
8503 peED.uid = uid;
8504 peED.body = NULL;
8506 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
8507 if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
8508 && (mc = mail_elt(ps_global->mail_stream, raw)))){
8509 char buf[256];
8511 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8512 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
8514 dprint((1, "ERROR fetching %s of msg %ld: %s",
8515 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
8516 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
8518 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8519 rv = TCL_ERROR;
8521 else{
8522 zero_atmts(ps_global->atmts);
8523 #ifdef SMIME
8524 if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
8525 ps_global->smime->need_passphrase = 0;
8527 fiddle_smime_message(peED.body, raw);
8528 #endif
8529 describe_mime(peED.body, "", 1, 1, 0, flags);
8533 /* NO HANDLES init_handles(&peED.handles);*/
8536 * Collect header pieces into lists via the passed custom formatter. Collect
8537 * everything else in the storage object passed. The latter should only end up
8538 * with raw header data.
8540 * BUG: DEAL WITH COLORS
8542 if(rv == TCL_OK){
8543 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
8544 if(format_header(ps_global->mail_stream, raw, NULL, peED.env, &h,
8545 NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){
8546 char buf[256];
8548 snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid));
8549 dprint((1, buf));
8551 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8552 rv = TCL_ERROR;
8556 peInterpFlush();
8557 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj);
8559 so_give(&peED.store);
8560 return(rv);
8563 void
8564 peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc, long int which, char *oacs, int flags)
8566 char *p2, buftmp[MAILTMPLEN];
8567 Tcl_Obj *objHdr;
8569 if(!e)
8570 return;
8572 if((which & FE_DATE) && e->date) {
8573 if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8574 snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date);
8575 buftmp[sizeof(buftmp)-1] = '\0';
8576 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp);
8577 peFormatEnvelopeText("Date", p2);
8579 /* BUG: how does error feedback bubble back up? */
8582 if((which & FE_FROM) && e->from)
8583 peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc);
8585 if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from)))
8586 peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc);
8588 if((which & FE_TO) && e->to)
8589 peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc);
8591 if((which & FE_CC) && e->cc)
8592 peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc);
8594 if((which & FE_BCC) && e->bcc)
8595 peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc);
8597 if((which & FE_RETURNPATH) && e->return_path)
8598 peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc);
8600 if((which & FE_NEWSGROUPS) && e->newsgroups)
8601 peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc);
8603 if((which & FE_FOLLOWUPTO) && e->followup_to)
8604 peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc);
8606 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
8607 if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8608 char *freeme = NULL;
8610 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000),
8611 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject),
8612 SIZEOF_20KBUF-10000);
8614 if(flags & FM_DISPLAY
8615 && (ps_global->display_keywords_in_subject
8616 || ps_global->display_keywordinits_in_subject)){
8618 /* don't bother if no keywords are defined */
8619 if(some_user_flags_defined(s))
8620 p2 = freeme = prepend_keyword_subject(s, n, p2,
8621 ps_global->display_keywords_in_subject ? KW : KWInit,
8622 NULL, ps_global->VAR_KW_BRACES);
8625 peFormatEnvelopeText("Subject", p2);
8627 if(freeme)
8628 fs_give((void **) &freeme);
8632 if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from)))
8633 peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc);
8635 if((which & FE_MESSAGEID) && e->message_id){
8636 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
8637 peFormatEnvelopeText("Message-ID", p2);
8640 if((which & FE_INREPLYTO) && e->in_reply_to){
8641 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
8642 peFormatEnvelopeText("In-Reply-To", p2);
8645 if((which & FE_REFERENCES) && e->references) {
8646 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
8647 peFormatEnvelopeText("References", p2);
8653 * appends caller's result with: {"text" field_name {field_value}}
8655 void
8656 peFormatEnvelopeText(char *field_name, char *field_value)
8658 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value);
8663 * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }}
8664 * {"rawaddr" field_name {{raw_address} ... }}
8666 void
8667 peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
8668 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
8670 char *ptmp, *mtmp, *atype = "addr";
8671 int group = 0;
8672 ADDRESS *atmp;
8673 Tcl_Obj *objAddrList = NULL;
8674 STORE_S *tso;
8675 gf_io_t tpc;
8676 extern const char *rspecials;
8677 extern const char *rspecials_minus_quote_and_dot;
8679 if(!addr)
8680 return;
8683 * quickly run down address list to make sure none are patently bogus.
8684 * If so, just blat raw field out.
8686 for(atmp = addr; stream && atmp; atmp = atmp->next)
8687 if(atmp->host && atmp->host[0] == '.'){
8688 char *field, *fields[2];
8690 atype = "rawaddr";
8691 if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL)
8692 return; /* BUG: handle list creation failure */
8694 fields[1] = NULL;
8695 fields[0] = cpystr(field_name);
8696 if((ptmp = strchr(fields[0], ':')) != NULL)
8697 *ptmp = '\0';
8699 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
8700 char *h, *t;
8702 for(t = h = field; *h ; t++)
8703 if(*t == '\015' && *(t+1) == '\012'){
8704 *t = '\0'; /* tie off line */
8706 Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8708 if(!*(h = (++t) + 1)) /* set new h and skip CRLF */
8709 break; /* no more to write */
8711 else if(!*t){ /* shouldn't happen much */
8712 if(h != t)
8713 Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8715 break;
8718 fs_give((void **)&field);
8721 fs_give((void **)&fields[0]);
8724 if(!objAddrList){
8725 if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL)
8726 return; /* BUG: handle list creation failure */
8728 gf_set_so_writec(&tpc, tso);
8730 while(addr){
8732 atmp = addr->next; /* remember what's next */
8733 addr->next = NULL;
8734 if(!addr->host && addr->mailbox){
8735 mtmp = addr->mailbox;
8736 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
8737 (unsigned char *)tmp_20k_buf,
8738 SIZEOF_20KBUF, addr->mailbox));
8741 ptmp = addr->personal; /* RFC 1522 personal name? */
8742 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
8743 tmp_20k_buf[10000-1] = '\0';
8746 /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */
8747 if (addr->host) { /* ordinary address? */
8748 if (!(addr->personal || addr->adl)){
8749 so_seek(tso, 0L, 0);
8750 pine_rfc822_address (addr, tpc);
8751 peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8753 else { /* no, must use phrase <route-addr> form */
8754 Tcl_Obj *objTmp;
8756 if (addr->personal){
8757 so_seek(tso, 0L, 0);
8758 pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc);
8759 objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso));
8762 so_seek(tso, 0L, 0);
8763 pine_rfc822_address(addr, tpc);
8764 peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8767 if(group)
8768 group++;
8770 else if (addr->mailbox) { /* start of group? */
8771 so_seek(tso, 0L, 0);
8772 /* yes, write group name */
8773 pine_rfc822_cat (addr->mailbox, rspecials, tpc);
8774 peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), "");
8775 group = 1; /* in a group */
8777 else if (group) { /* must be end of group (but be paranoid) */
8778 peAppListF(peED.interp, objAddrList, "%s%s", "", ";");
8779 group = 0; /* no longer in that group */
8782 addr->personal = ptmp; /* restore old personal ptr */
8783 if(!addr->host && addr->mailbox){
8784 fs_give((void **)&addr->mailbox);
8785 addr->mailbox = mtmp;
8788 addr->next = atmp;
8789 addr = atmp;
8792 gf_clear_so_writec(tso);
8793 so_give(&tso);
8796 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList);
8801 * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }}
8803 void
8804 peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_io_t pc)
8806 char buf[MAILTMPLEN];
8807 int llen;
8808 char *next_ng;
8809 Tcl_Obj *objNewsgroups;
8811 /* BUG: handle list creation failure */
8812 if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL)
8813 return;
8815 llen = strlen(field_name);
8816 while(*newsgrps){
8817 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++)
8820 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
8821 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
8823 Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1));
8825 newsgrps = next_ng;
8826 if(*newsgrps)
8827 newsgrps++;
8830 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups);
8835 peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8837 MESSAGECACHE *mc;
8838 ATTACH_S *a;
8839 BODY *body;
8840 Tcl_Obj *objAtt, *tObj, *stObj, *fnObj;
8841 int flags, rv = TCL_OK;
8842 long raw;
8844 peED.interp = interp;
8845 peED.obj = Tcl_GetObjResult(interp);
8847 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER;
8849 raw = peSequenceNumber(uid);
8851 if(peED.uid != uid){
8852 memset(&peED, 0, sizeof(peED));
8854 peED.uid = uid;
8856 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
8857 if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
8858 && (mc = mail_elt(ps_global->mail_stream, raw)))){
8859 char buf[256];
8861 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8862 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
8864 dprint((1, "ERROR fetching %s of msg %ld: %s",
8865 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
8866 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
8868 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8869 rv = TCL_ERROR;
8871 else{
8872 zero_atmts(ps_global->atmts);
8873 #ifdef SMIME
8874 if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
8875 ps_global->smime->need_passphrase = 0;
8877 fiddle_smime_message(peED.body, raw);
8878 #endif
8879 describe_mime(peED.body, "", 1, 1, 0, flags);
8883 /* package up attachment list */
8884 for(a = ps_global->atmts; rv == TCL_OK && a->description != NULL; a++)
8885 if((objAtt = Tcl_NewListObj(0, NULL)) != NULL
8886 && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){
8887 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
8889 if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK
8890 && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK
8891 && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK
8892 && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK
8893 && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK
8894 && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK
8895 && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK))
8896 rv = TCL_ERROR;
8898 else
8899 rv = TCL_ERROR;
8901 return(rv);
8906 peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8908 MESSAGECACHE *mc;
8909 int flags, rv = TCL_OK;
8910 long raw;
8911 char *color;
8913 peED.interp = interp;
8914 peED.obj = Tcl_GetObjResult(interp);
8916 if(peED.store)
8917 so_seek(peED.store, 0L, 0);
8918 else
8919 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8921 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8923 if(objc == 1 && objv[0]){ /* flags */
8924 int i, nFlags;
8925 Tcl_Obj **objFlags;
8926 char *flagstr;
8928 Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags);
8929 for(i = 0; i < nFlags; i++){
8930 if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
8931 rv = TCL_ERROR;
8934 if(!strucmp(flagstr, "html"))
8935 flags |= (FM_HTML | FM_HIDESERVER);
8936 else if(!strucmp(flagstr, "images"))
8937 flags |= (FM_HTMLIMAGES);
8941 peED.color.fg[0] = '\0';
8942 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8943 peInterpWritec(TAG_EMBED);
8944 peInterpWritec(TAG_FGCOLOR);
8945 gf_puts(color, peInterpWritec);
8946 strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
8949 peED.color.bg[0] = '\0';
8950 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8951 peInterpWritec(TAG_EMBED);
8952 peInterpWritec(TAG_BGCOLOR);
8953 gf_puts(color, peInterpWritec);
8954 strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
8957 peInterpFlush();
8959 init_handles(&peED.handles);
8961 raw = peSequenceNumber(uid);
8963 if(peED.uid != uid){
8964 peED.uid = uid;
8965 peED.body = NULL;
8967 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
8968 if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
8969 && (mc = mail_elt(ps_global->mail_stream, raw)))){
8970 char buf[256];
8972 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8973 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
8975 dprint((1, "ERROR fetching %s of msg %ld: %s",
8976 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
8977 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
8979 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8980 rv = TCL_ERROR;
8982 else{
8983 zero_atmts(ps_global->atmts);
8984 #ifdef SMIME
8985 if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
8986 ps_global->smime->need_passphrase = 0;
8988 fiddle_smime_message(peED.body, raw);
8989 #endif
8990 describe_mime(peED.body, "", 1, 1, 0, flags);
8994 /* format message body */
8995 if(rv == TCL_OK){
8996 HEADER_S h;
8997 char *errstr;
8999 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
9000 #ifdef SMIME
9001 /* kind of a hack, the description maybe shouldn't be in the editorial stuff */
9002 if(ps_global->smime && ps_global->smime->need_passphrase)
9003 flags &= ~FM_NOEDITORIAL;
9004 #endif
9005 if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){
9006 gf_puts(errstr, peInterpWritec);
9007 rv = TCL_ERROR;
9011 peInterpFlush();
9013 so_give(&peED.store);
9014 free_handles(&peED.handles);
9015 return(rv);
9020 peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9022 MESSAGECACHE *mc;
9023 ENVELOPE *env;
9024 BODY *body;
9025 int flags;
9026 long raw;
9027 char *color;
9029 memset(&peED, 0, sizeof(peED));
9030 peED.interp = interp;
9031 peED.obj = Tcl_GetObjResult(interp);
9032 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
9034 peED.color.fg[0] = '\0';
9035 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
9036 peInterpWritec(TAG_EMBED);
9037 peInterpWritec(TAG_FGCOLOR);
9038 gf_puts(color, peInterpWritec);
9039 strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
9042 peED.color.bg[0] = '\0';
9043 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
9044 peInterpWritec(TAG_EMBED);
9045 peInterpWritec(TAG_BGCOLOR);
9046 gf_puts(color, peInterpWritec);
9047 strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
9050 raw = peSequenceNumber(peED.uid = uid);
9051 body = NULL;
9052 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
9053 if(!((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body))
9054 && (mc = mail_elt(ps_global->mail_stream, raw)))){
9055 char buf[256];
9057 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
9058 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
9060 dprint((1, "ERROR fetching %s of msg %ld: %s",
9061 env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
9062 ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
9064 Tcl_SetResult(interp, buf, TCL_VOLATILE);
9065 return(TCL_ERROR);
9068 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
9070 init_handles(&peED.handles);
9072 (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec);
9074 peInterpFlush();
9076 so_give(&peED.store);
9077 free_handles(&peED.handles);
9078 return(TCL_OK);
9083 * peMessagePartFromCID - return part number assoc'd with given uid and CID
9086 peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9088 char *cid, sect_buf[256];
9089 long raw;
9090 ENVELOPE *env;
9091 BODY *body;
9093 raw = peSequenceNumber(peED.uid = uid);
9094 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
9096 if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){
9097 if((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body)) != NULL){
9098 sect_buf[0] = '\0';
9099 if(peLocateBodyByCID(cid, sect_buf, body)){
9100 Tcl_SetResult(interp, sect_buf, TCL_VOLATILE);
9103 else{
9104 Tcl_SetResult(interp, ps_global->last_error[0] ? ps_global->last_error : "Error getting CID", TCL_VOLATILE);
9105 return(TCL_ERROR);
9109 return(TCL_OK);
9114 peLocateBodyByCID(char *cid, char *section, BODY *body)
9116 if(body->type == TYPEMULTIPART){
9117 char subsection[256], *subp;
9118 int n;
9119 PART *part = body->nested.part;
9121 if(!(part = body->nested.part))
9122 return(0);
9124 subp = subsection;
9125 if(section && *section){
9126 for(n = 0;
9127 n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
9130 *subp++ = '.';
9133 n = 1;
9134 do {
9135 sprintf(subp, "%d", n++);
9136 if(peLocateBodyByCID(cid, subsection, &part->body)){
9137 strcpy(section, subsection);
9138 return(1);
9141 while((part = part->next) != NULL);
9143 return(0);
9146 return((body && body->id) ? !strcmp(cid, body->id) : 0);
9151 * peGetFlag - Return 1 or 0 based on requested flags current state
9153 * Params: argv[0] == flagname
9156 peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9158 char *flagname;
9160 Tcl_SetResult(interp,
9161 int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9162 ? peIsFlagged(ps_global->mail_stream, uid, flagname)
9163 : 0),
9164 TCL_VOLATILE);
9165 return(TCL_OK);
9170 peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname)
9172 MESSAGECACHE *mc;
9173 long raw = peSequenceNumber(uid);
9175 if(!((mc = mail_elt(stream, raw)) && mc->valid)){
9176 mail_fetch_flags(stream, ulong2string(uid), FT_UID);
9177 mc = mail_elt(stream, raw);
9180 if(!strucmp(flagname, "deleted"))
9181 return(mc->deleted);
9183 if(!strucmp(flagname, "new"))
9184 return(!mc->seen);
9186 if(!strucmp(flagname, "important"))
9187 return(mc->flagged);
9189 if(!strucmp(flagname, "answered"))
9190 return(mc->answered);
9192 if(!strucmp(flagname, "recent"))
9193 return(mc->recent);
9195 return(0);
9200 * peSetFlag - Set requested flags value to 1 or 0
9202 * Params: abjv[0] == flagname
9203 * objv[1] == newvalue
9206 peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9208 char *flagname, *flagstr = NULL;
9209 int value;
9211 if((flagname = Tcl_GetStringFromObj(objv[0], NULL))
9212 && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){
9213 if(!strucmp(flagname, "deleted")){
9214 flagstr = "\\DELETED";
9216 else if(!strucmp(flagname, "new")){
9217 flagstr = "\\SEEN";
9218 value = !value;
9220 else if(!strucmp(flagname, "important")){
9221 flagstr = "\\FLAGGED";
9223 else if(!strucmp(flagname, "answered")){
9224 flagstr = "\\ANSWERED";
9226 else if(!strucmp(flagname, "recent")){
9227 flagstr = "\\RECENT";
9230 if(flagstr){
9231 ps_global->c_client_error[0] = '\0';
9232 mail_flag(ps_global->mail_stream,
9233 ulong2string(uid),
9234 flagstr, (value ? ST_SET : 0L) | ST_UID);
9235 if(ps_global->c_client_error[0] != '\0'){
9236 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s",
9237 ps_global->c_client_error);
9238 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
9239 return(TCL_ERROR);
9244 Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC);
9245 return(TCL_OK);
9250 * peMsgSelect - Return 1 or 0 based on whether given UID is selected
9252 * Params: argv[0] == selected
9255 peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9257 int value;
9259 if(objc == 1 && objv[0]){
9260 if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){
9261 if(value){
9262 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
9263 peMessageNumber(uid), MN_SLCT, 1);
9264 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
9265 peMessageNumber(uid), MN_HIDE, 0);
9266 } else {
9267 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
9268 peMessageNumber(uid), MN_SLCT, 0);
9269 /* if zoomed, lite hidden bit */
9270 if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
9271 set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
9272 peMessageNumber(uid), MN_HIDE, 1);
9276 else{
9277 Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC);
9278 return(TCL_ERROR);
9282 Tcl_SetResult(interp,
9283 (get_lflag(ps_global->mail_stream, NULL,
9284 peSequenceNumber(uid),
9285 MN_SLCT))
9286 ? "1" : "0",
9287 TCL_VOLATILE);
9288 return(TCL_OK);
9293 * peAppendIndexParts - append list of digested index pieces to given object
9295 * Params:
9299 peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9301 Tcl_Obj *objField, *objElement, *objp;
9302 ICE_S *h;
9303 IFIELD_S *f;
9304 IELEM_S *ie;
9307 if((h = build_header_work(ps_global, ps_global->mail_stream,
9308 sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
9309 gPeITop, gPeICount, fetched)) != NULL){
9310 for(f = h->ifield; f; f = f->next){
9312 if((objField = Tcl_NewListObj(0, NULL)) == NULL)
9313 return(TCL_ERROR);
9315 for(ie = f->ielem; ie ; ie = ie->next){
9317 if((objElement = Tcl_NewListObj(0, NULL)) == NULL)
9318 return(TCL_ERROR);
9320 if(ie->datalen){
9321 /* FIRST: DATA */
9322 #if INTERNAL_INDEX_TRUNCATE
9323 char *ep;
9325 ep = (char *) fs_get((ie->datalen + 1) * sizeof(char));
9326 sprintf(ep, "%.*s", ie->wid, ie->data);
9328 /* and other stuff to pack trunc'd element into a new object */
9329 #endif
9331 objp = Tcl_NewStringObj(ie->data, ie->datalen);
9333 else
9334 objp = Tcl_NewStringObj("", -1);
9336 if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9337 return(TCL_ERROR);
9339 if(ie->color){
9340 Tcl_Obj *objColor;
9341 char hexcolor[32];
9343 if((objp = Tcl_NewListObj(0, NULL)) == NULL)
9344 return(TCL_ERROR);
9346 hex_colorstr(hexcolor, ie->color->fg);
9347 objColor = Tcl_NewStringObj(hexcolor, -1);
9348 if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9349 return(TCL_ERROR);
9351 hex_colorstr(hexcolor, ie->color->bg);
9352 objColor = Tcl_NewStringObj(hexcolor, -1);
9353 if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9354 return(TCL_ERROR);
9356 else
9357 objp = Tcl_NewStringObj("", -1);
9359 if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9360 return(TCL_ERROR);
9363 * IF we ever want to map the thread characters into nice
9364 * graphical symbols or take advantage of features like clicking
9365 * on a thread element to collapse and such, we need to have
9366 * element tagging. That's what the object creation and append
9367 * are placeholders for
9369 switch(ie->type){
9370 case eThreadInfo :
9371 objp = Tcl_NewStringObj("threadinfo", -1);
9372 break;
9373 case eText :
9374 objp = NULL;
9375 break;
9376 default :
9377 objp = Tcl_NewStringObj(int2string(ie->type), -1);
9378 break;
9381 if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9382 return(TCL_ERROR);
9384 if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK)
9385 return(TCL_ERROR);
9388 if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){
9389 return(TCL_ERROR);
9394 return(TCL_OK);
9399 * peAppendIndexColor - append index line's foreground/background color
9401 * Params:
9405 peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9407 char hexfg[32], hexbg[32];
9408 ICE_S *h;
9410 if((h = build_header_work(ps_global, ps_global->mail_stream,
9411 sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
9412 gPeITop, gPeICount, fetched))
9413 && h->color_lookup_done
9414 && h->linecolor){
9416 hex_colorstr(hexfg, h->linecolor->fg);
9417 hex_colorstr(hexbg, h->linecolor->bg);
9419 return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg));
9422 return(peAppListF(interp, aObj, "%s", ""));
9427 * peMessageStatusBits - return list flags indicating pine status bits
9429 * Params:
9431 * Returns: list of lists where:
9432 * * the first element is the list of
9433 * field elements data
9434 * * the second element is a two element
9435 * list containing the lines foreground
9436 * and background colors
9439 peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9441 Tcl_SetResult(interp,
9442 peMsgStatBitString(ps_global, ps_global->mail_stream,
9443 sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
9444 gPeITop, gPeICount, NULL),
9445 TCL_STATIC);
9446 return(TCL_OK);
9450 char *
9451 peMsgStatBitString(struct pine *state,
9452 MAILSTREAM *stream,
9453 MSGNO_S *msgmap,
9454 long msgno,
9455 long top_msgno,
9456 long msgcount,
9457 int *fetched)
9459 static char buf[36];
9460 int i;
9461 long raw;
9462 MESSAGECACHE *mc;
9463 ICE_S *h;
9465 raw = mn_m2raw(msgmap, msgno);
9466 if((h = build_header_work(state, stream, msgmap,
9467 msgno, top_msgno, msgcount, fetched))
9468 && (mc = mail_elt(stream, raw))){
9469 /* return a string representing a bit field where:
9470 index meaning
9471 ----- -------
9472 0 "New"
9473 1 deleted
9474 2 answered
9475 3 flagged
9476 4 to us
9477 5 cc us
9478 6 recent
9479 7 forwarded
9480 8 attachments
9482 i = 0;
9483 buf[i++] = (mc->seen) ? '0' : '1';
9484 buf[i++] = (mc->deleted) ? '1' : '0';
9485 buf[i++] = (mc->answered) ? '1' : '0';
9486 buf[i++] = (mc->flagged) ? '1' : '0';
9487 buf[i++] = (h->to_us) ? '1' : '0';
9488 buf[i++] = (h->cc_us) ? '1' : '0';
9489 buf[i++] = (mc->recent) ? '1' : '0';
9490 buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0';
9491 buf[i++] = '0';
9492 buf[i++] = '\0';
9494 return(buf);
9497 return("100000000");
9501 Tcl_Obj *
9502 peMsgStatNameList(Tcl_Interp *interp,
9503 struct pine *state,
9504 MAILSTREAM *stream,
9505 MSGNO_S *msgmap,
9506 long msgno,
9507 long top_msgno,
9508 long msgcount,
9509 int *fetched)
9511 Tcl_Obj *objList;
9512 long raw;
9513 MESSAGECACHE *mc;
9514 ICE_S *h;
9516 objList = Tcl_NewListObj(0, NULL);
9517 raw = mn_m2raw(msgmap, msgno);
9518 if((h = build_header_work(state, stream, msgmap,
9519 msgno, top_msgno, msgcount, fetched))
9520 && (mc = mail_elt(stream, raw))){
9521 if(!mc->seen)
9522 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1));
9524 if(mc->deleted)
9525 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1));
9527 if(mc->answered)
9528 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1));
9530 if(mc->flagged)
9531 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1));
9533 if(h->to_us)
9534 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1));
9536 if(h->cc_us)
9537 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1));
9539 if(mc->recent)
9540 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1));
9542 if(user_flag_is_set(stream, raw, FORWARDED_FLAG))
9543 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1));
9545 if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
9546 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1));
9549 return(objList);
9554 * peReplyHeaders - return subject used in reply to given message
9556 * Params:
9558 * Returns: list of header value pairs where headers are:
9559 * In-Reply-To:, Subject:, Cc:
9563 peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9565 long raw;
9566 int flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE;
9567 char *errmsg = NULL, *fcc = NULL, *sect = NULL;
9568 ENVELOPE *env, *outgoing;
9569 BODY *body = NULL;
9570 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
9572 saved_from = (ADDRESS *) NULL;
9573 saved_to = (ADDRESS *) NULL;
9574 saved_cc = (ADDRESS *) NULL;
9575 saved_resent = (ADDRESS *) NULL;
9577 raw = peSequenceNumber(uid);
9579 /* if we're given a valid section number that
9580 * corresponds to a valid msg/rfc822 body part
9581 * then set up headers in attached message.
9583 if(objc == 1 && objv[0]
9584 && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9585 && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
9586 && body->type == TYPEMESSAGE
9587 && !strucmp(body->subtype, "rfc822")){
9588 env = body->nested.msg->env;
9590 else{
9591 sect = NULL;
9592 env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
9595 if(env){
9596 if(!reply_harvest(ps_global, raw, sect, env,
9597 &saved_from, &saved_to, &saved_cc,
9598 &saved_resent, &flags)){
9600 Tcl_SetResult(interp, "", TCL_STATIC);
9601 return(TCL_ERROR);
9604 outgoing = mail_newenvelope();
9606 reply_seed(ps_global, outgoing, env,
9607 saved_from, saved_to, saved_cc, saved_resent,
9608 &fcc, flags, &errmsg);
9609 if(errmsg){
9610 if(*errmsg){
9611 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
9614 fs_give((void **)&errmsg);
9617 env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL);
9619 outgoing->subject = reply_subject(env->subject, NULL, 0);
9620 outgoing->in_reply_to = reply_in_reply_to(env);
9622 err = !(peAppListF(interp, Tcl_GetObjResult(interp),
9623 "%s%a", "to", outgoing->to) == TCL_OK
9624 && peAppListF(interp, Tcl_GetObjResult(interp),
9625 "%s%a", "cc", outgoing->cc) == TCL_OK
9626 && peAppListF(interp, Tcl_GetObjResult(interp),
9627 "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK
9628 && peAppListF(interp, Tcl_GetObjResult(interp),
9629 "%s%s", "subject",
9630 rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
9631 SIZEOF_20KBUF, outgoing->subject)) == TCL_OK
9632 && (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE));
9635 /* Fill in x-reply-uid data and append it */
9636 if(!err && ps_global->mail_stream->uid_validity){
9637 char *prefix = reply_quote_str(env);
9639 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "(%lu %s)(1 %lu %lu)%s",
9640 strlen(prefix), prefix,
9641 ps_global->mail_stream->uid_validity, uid,
9642 ps_global->mail_stream->mailbox);
9644 fs_give((void **) &prefix);
9646 err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9647 "x-reply-uid", tmp_20k_buf) != TCL_OK;
9650 mail_free_envelope(&outgoing);
9652 if(err)
9653 return(TCL_ERROR);
9655 else
9656 Tcl_SetResult(interp, "", TCL_VOLATILE);
9658 return(TCL_OK);
9664 * peReplyText - return subject used in reply to given message
9666 * Params:
9668 * Returns:
9672 peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9674 long msgno;
9675 char *prefix, *sect = NULL;
9676 int rv = TCL_OK;
9677 ENVELOPE *env;
9678 BODY *body = NULL, *orig_body;
9679 STORE_S *msgtext;
9680 REDRAFT_POS_S *redraft_pos = NULL;
9681 Tcl_Obj *objBody = NULL, *objAttach = NULL;
9683 msgno = peSequenceNumber(uid);
9685 if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9686 Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE);
9687 return(TCL_ERROR);
9690 /*--- Grab current envelope ---*/
9691 /* if we're given a valid section number that
9692 * corresponds to a valid msg/rfc822 body part
9693 * then set up to reply the attached message's
9694 * text.
9696 if(objc == 2 && objv[1]
9697 && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0'
9698 && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
9699 && body->type == TYPEMESSAGE
9700 && !strucmp(body->subtype, "rfc822")){
9701 env = body->nested.msg->env;
9702 orig_body = body->nested.msg->body;
9704 else{
9705 sect = NULL;
9706 env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
9707 if(!(env && orig_body)){
9708 Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9709 return(TCL_ERROR);
9713 if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9714 prefix = cpystr(prefix);
9715 else
9716 prefix = reply_quote_str(env);
9719 * BUG? Should there be some way to signal to reply_bddy
9720 * that we'd like it to produced format=flowed body text?
9721 * right now it's hardwired to in pine/reply.c
9724 if((body = reply_body(ps_global->mail_stream, env, orig_body,
9725 msgno, sect, msgtext, prefix,
9726 TRUE, NULL, TRUE, &redraft_pos)) != NULL){
9728 objBody = Tcl_NewListObj(0, NULL);
9730 peSoStrToList(interp, objBody, msgtext);
9732 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9734 /* sniff for attachments */
9735 objAttach = peMsgAttachCollector(interp, body);
9737 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9740 pine_free_body(&body);
9742 else{
9743 Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9744 rv = TCL_ERROR;
9747 fs_give((void **) &prefix);
9749 return(rv);
9754 peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so)
9756 char *sp, *ep;
9757 Tcl_Obj *objp;
9759 for(ep = (char *) so_text(so); *ep; ep++){
9760 sp = ep;
9762 while(*ep && *ep != '\n')
9763 ep++;
9765 objp = Tcl_NewStringObj(sp, ep - sp);
9767 if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK)
9768 return(FALSE);
9771 return(TRUE);
9776 * peForwardHeaders - return subject used in forward of given message
9778 * Params:
9780 * Returns:
9784 peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9787 int result;
9788 long raw;
9789 char *tmp, *sect = NULL;
9790 ENVELOPE *env;
9791 BODY *body;
9793 raw = peSequenceNumber(uid);
9795 /* if we're given a valid section number that
9796 * corresponds to a valid msg/rfc822 body part
9797 * then set up headers in attached message.
9799 if(objc == 1 && objv[0]
9800 && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9801 && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
9802 && body->type == TYPEMESSAGE
9803 && !strucmp(body->subtype, "rfc822")){
9804 env = body->nested.msg->env;
9806 else{
9807 sect = NULL;
9808 env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
9811 if(env){
9812 tmp = forward_subject(env, FS_NONE);
9813 result = peAppListF(interp, Tcl_GetObjResult(interp),
9814 "%s%s", "subject", tmp);
9815 fs_give((void **) &tmp);
9817 /* Fill in x-reply-uid data and append it */
9818 if(result == TCL_OK && ps_global->mail_stream->uid_validity){
9819 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s",
9820 ps_global->mail_stream->uid_validity, uid,
9821 ps_global->mail_stream->mailbox);
9822 result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9823 "x-reply-uid", tmp_20k_buf) != TCL_OK;
9826 return(result);
9829 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
9830 return(TCL_ERROR);
9836 * peForwardText - return body of message used in
9837 * forward of given message
9839 * Params:
9841 * Returns:
9845 peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9847 long msgno;
9848 char *bodtext, *p, *sect = NULL;
9849 int rv = TCL_OK;
9850 ENVELOPE *env;
9851 BODY *body, *orig_body;
9852 STORE_S *msgtext;
9853 Tcl_Obj *objBody = NULL, *objAttach = NULL;
9855 msgno = peSequenceNumber(uid);
9857 if(objc == 1 && objv[0])
9858 sect = Tcl_GetStringFromObj(objv[0], NULL);
9860 if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9861 Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE);
9862 return(TCL_ERROR);
9866 if(F_ON(F_FORWARD_AS_ATTACHMENT, ps_global)){
9867 PART **pp;
9868 long totalsize = 0L;
9870 /*---- New Body to start with ----*/
9871 body = mail_newbody();
9872 body->type = TYPEMULTIPART;
9874 /*---- The TEXT part/body ----*/
9875 body->nested.part = mail_newbody_part();
9876 body->nested.part->body.type = TYPETEXT;
9877 body->nested.part->body.contents.text.data = (unsigned char *) msgtext;
9879 pp = &(body->nested.part->next);
9881 /*---- The Message body subparts ----*/
9882 env = pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
9884 if(forward_mime_msg(ps_global->mail_stream, msgno,
9885 (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){
9886 totalsize = (*pp)->body.size.bytes;
9887 pp = &((*pp)->next);
9890 else{
9891 /*--- Grab current envelope ---*/
9892 /* if we're given a valid section number that
9893 * corresponds to a valid msg/rfc822 body part
9894 * then set up to forward the attached message's
9895 * text.
9898 if(sect && *sect != '\0'
9899 && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
9900 && body->type == TYPEMESSAGE
9901 && !strucmp(body->subtype, "rfc822")){
9902 env = body->nested.msg->env;
9903 orig_body = body->nested.msg->body;
9905 else{
9906 sect = NULL;
9907 env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
9908 if(!(env && orig_body)){
9909 Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9910 return(TCL_ERROR);
9914 body = forward_body(ps_global->mail_stream, env, orig_body,
9915 msgno, sect, msgtext, FWD_NONE);
9918 if(body){
9919 bodtext = (char *) so_text(msgtext);
9921 objBody = Tcl_NewListObj(0, NULL);
9923 for(p = bodtext; *p; p++){
9924 Tcl_Obj *objp;
9926 bodtext = p;
9927 while(*p && *p != '\n')
9928 p++;
9930 objp = Tcl_NewStringObj(bodtext, p - bodtext);
9932 Tcl_ListObjAppendElement(interp, objBody, objp);
9935 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9937 /* sniff for attachments */
9938 objAttach = peMsgAttachCollector(interp, body);
9939 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9941 pine_free_body(&body);
9943 else{
9944 Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9945 rv = TCL_ERROR;
9948 return(rv);
9954 * peDetach -
9956 * Params: argv[0] == attachment part number
9957 * argv[1] == directory to hold tmp file
9959 * Returns: list containing:
9961 * 0) response: OK or ERROR
9962 * if OK
9963 * 1) attachment's mime type
9964 * 2) attachment's mime sub-type
9965 * 3) attachment's size in bytes (decoded)
9966 * 4) attachment's given file name (if any)
9967 * 5) tmp file holding raw attachment data
9970 peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9972 char *part, *err, *tfd, *tfn = NULL, *filename;
9973 long raw;
9974 gf_io_t pc;
9975 BODY *body;
9976 STORE_S *store;
9977 Tcl_Obj *rvobj, *tObj, *stObj, *fnObj;
9979 if((part = Tcl_GetStringFromObj(objv[0], NULL))
9980 && (raw = peSequenceNumber(uid))
9981 && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
9983 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
9985 err = NULL;
9986 if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){
9987 tfn = temp_nam(tfd = NULL, "pd");
9989 else if(is_writable_dir(tfd) == 0){
9990 tfn = temp_nam(tfd, "pd");
9992 else
9993 tfn = tfd;
9995 filename = Tcl_GetStringFromObj(fnObj, NULL);
9996 dprint((5, "PEDetach(name: %s, tmpfile: %s)",
9997 filename ? filename : "<null>", tfn));
9999 if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){
10000 gf_set_so_writec(&pc, store);
10001 err = detach(ps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0);
10002 gf_clear_so_writec(store);
10003 so_give(&store);
10005 else
10006 err = "Can't allocate internal storage";
10008 else
10009 err = "Can't get message data";
10011 if(err){
10012 if(tfn)
10013 unlink(tfn);
10015 dprint((1, "PEDetach FAIL: %d: %s", errno, err));
10016 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err);
10017 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
10018 return(TCL_ERROR);
10021 /* package up response */
10022 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10023 Tcl_NewListObj(1, &tObj));
10025 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10026 Tcl_NewListObj(1, &stObj));
10028 rvobj = Tcl_NewLongObj(name_file_size(tfn));
10029 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10030 Tcl_NewListObj(1, &rvobj));
10031 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10032 Tcl_NewListObj(1, &fnObj));
10033 rvobj = Tcl_NewStringObj(tfn, -1);
10034 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10035 Tcl_NewListObj(1, &rvobj));
10037 return(TCL_OK);
10042 * peAttachInfo -
10044 * Params: argv[0] == attachment part number
10046 * Returns: list containing:
10048 * 0) response: OK or ERROR
10049 * if OK
10050 * 1) attachment's mime type
10051 * 2) attachment's mime sub-type
10052 * 3) attachment's size in bytes (decoded)
10053 * 4) attachment's given file name (if any)
10054 * 5) tmp file holding raw attachment data
10057 peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10059 char *part;
10060 long raw;
10061 BODY *body;
10062 PARMLIST_S *plist;
10063 Tcl_Obj *tObj, *stObj, *fnObj;
10065 if((part = Tcl_GetStringFromObj(objv[0], NULL))
10066 && (raw = peSequenceNumber(uid))
10067 && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
10069 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
10071 else{
10072 Tcl_SetResult(interp, "Can't get message data", TCL_STATIC);
10073 return(TCL_ERROR);
10076 /* package up response */
10078 /* filename */
10079 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj);
10081 /* type */
10082 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
10084 /* subtype */
10085 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj);
10087 /* encoding */
10088 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10089 Tcl_NewStringObj((body->encoding < ENCMAX)
10090 ? body_encodings[body->encoding]
10091 : "Unknown", -1));
10093 /* parameters */
10094 if((plist = rfc2231_newparmlist(body->parameter)) != NULL){
10095 Tcl_Obj *lObj = Tcl_NewListObj(0, NULL);
10096 Tcl_Obj *pObj[2];
10098 while(rfc2231_list_params(plist)){
10099 pObj[0] = Tcl_NewStringObj(plist->attrib, -1);
10100 pObj[1] = Tcl_NewStringObj(plist->value, -1);
10101 Tcl_ListObjAppendElement(interp, lObj,
10102 Tcl_NewListObj(2, pObj));
10105 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj);
10106 rfc2231_free_parmlist(&plist);
10109 /* size guesstimate */
10110 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10111 Tcl_NewStringObj(comatose((body->encoding == ENCBASE64)
10112 ? ((body->size.bytes * 3)/4)
10113 : body->size.bytes), -1));
10115 return(TCL_OK);
10120 * peSaveDefault - Default saved file name for the given message
10121 * specified collection/folder
10123 * Params:
10125 * Returns: name of saved message folder or empty string
10129 peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10131 char *folder;
10132 CONTEXT_S *cntxt, *cp;
10133 int colid;
10134 long rawno;
10135 ENVELOPE *env;
10137 if(uid){
10138 if(!(env = pine_mail_fetchstructure(ps_global->mail_stream,
10139 rawno = peSequenceNumber(uid),
10140 NULL))){
10141 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
10142 return(TCL_ERROR);
10145 else
10146 env = NULL;
10148 if(!(folder = save_get_default(ps_global, env, rawno, NULL, &cntxt))){
10149 Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE);
10150 return(TCL_ERROR); /* message expunged! */
10153 for(colid = 0, cp = ps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next)
10156 if(!cp)
10157 colid = 0;
10159 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10160 Tcl_NewIntObj(colid));
10161 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10162 Tcl_NewStringObj(folder, -1));
10163 return(TCL_OK);
10168 * peSaveWork - Save message with given UID in current folder to
10169 * specified collection/folder
10171 * Params: argv[0] == destination context number
10172 * argv[1] == testination foldername
10177 peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags)
10179 int flgs = 0, i, colid;
10180 char *folder, *err = NULL;
10181 CONTEXT_S *cp;
10183 if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){
10184 if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
10185 mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
10186 for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
10187 if(i == colid)
10188 break;
10190 if(cp){
10191 if(!READONLY_FOLDER(ps_global->mail_stream)
10192 && (sflags & PSW_COPY) != PSW_COPY
10193 && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, ps_global)))
10194 flgs |= SV_DELETE;
10196 if(colid == 0 && !strucmp(folder, "inbox"))
10197 flgs |= SV_INBOXWOCNTXT;
10199 if(sflags & (PSW_COPY | PSW_MOVE))
10200 flgs |= SV_FIX_DELS;
10202 i = save(ps_global, ps_global->mail_stream,
10203 cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
10205 if(i == mn_total_cur(sp_msgmap(ps_global->mail_stream))){
10206 if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 1L){
10207 if(ps_global->context_list->next
10208 && context_isambig(folder)){
10209 char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders";
10210 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
10211 "Message %s %s to \"%.15s%s\" in <%.15s%s>",
10212 long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
10213 (sflags & PSW_MOVE) ? "moved" : "copied",
10214 folder,
10215 (strlen(folder) > 15) ? "..." : "",
10216 tag,
10217 (strlen(tag) > 15) ? "..." : "");
10219 else
10220 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
10221 "Message %s %s to folder \"%.27s%s\"",
10222 long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
10223 (sflags & PSW_MOVE) ? "moved" : "copied",
10224 folder,
10225 (strlen(folder) > 27) ? "..." : "");
10227 else{
10228 /* with mn_set_cur above, this *should not* happen */
10229 Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE);
10230 return(TCL_ERROR);
10233 if(sflags == PSW_NONE && (flgs & SV_DELETE)){
10234 strncat(tmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
10235 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
10238 q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
10239 return(TCL_OK);
10242 err = ps_global->last_error;
10244 else
10245 err = "open: Unrecognized collection ID";
10247 else
10248 err = "open: Can't read folder";
10250 else
10251 err = "open: Can't get collection ID";
10253 Tcl_SetResult(interp, err, TCL_VOLATILE);
10254 return(TCL_ERROR);
10258 * peSave - Save message with given UID in current folder to
10259 * specified collection/folder
10261 * Params: argv[0] == destination context number
10262 * argv[1] == testination foldername
10264 * NOTE: just a wrapper around peSaveWork
10267 peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10269 return(peSaveWork(interp, uid, objc, objv, PSW_NONE));
10274 * peCopy - Copy message with given UID in current folder to
10275 * specified collection/folder
10277 * Params: argv[0] == destination context number
10278 * argv[1] == testination foldername
10280 * NOTE: just a wrapper around peSaveWork that makes sure
10281 * delete-on-save is NOT set
10284 peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10286 return(peSaveWork(interp, uid, objc, objv, PSW_COPY));
10291 * peMove - Move message with given UID in current folder to
10292 * specified collection/folder
10294 * Params: argv[0] == destination context number
10295 * argv[1] == testination foldername
10297 * NOTE: just a wrapper around peSaveWork that makes sure
10298 * delete-on-save IS set so it can be expunged
10301 peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10303 return(peSaveWork(interp, uid, objc, objv, PSW_MOVE));
10308 * peGotoDefault - Default Goto command file name for the given message
10309 * specified collection/folder
10311 * Params:
10313 * Returns: name of Goto command default folder or empty string
10317 peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv)
10319 char *folder = NULL;
10320 CONTEXT_S *cntxt, *cp;
10321 int colid, inbox;
10323 cntxt = broach_get_folder(ps_global->context_current, &inbox, &folder);
10325 for(colid = 0, cp = ps_global->context_list; cp != cntxt ; colid++, cp = cp->next)
10328 if(!cp)
10329 colid = 0;
10331 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10332 Tcl_NewIntObj(colid));
10333 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10334 Tcl_NewStringObj(folder ? folder : "", -1));
10335 return(TCL_OK);
10340 * peReplyQuote -
10342 * Params: argv[0] == attachment part number
10344 * Returns: list containing:
10348 peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10350 char *quote;
10351 ENVELOPE *env;
10353 if(uid){
10354 if((env = pine_mail_fetchstructure(ps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){
10355 quote = reply_quote_str(env);
10356 Tcl_SetResult(interp, quote, TCL_VOLATILE);
10357 fs_give((void **) &quote);
10359 else{
10360 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
10361 return(TCL_ERROR);
10364 else
10365 Tcl_SetResult(interp, "> ", TCL_VOLATILE);
10367 return(TCL_OK);
10371 void
10372 peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp)
10374 char *ptype = NULL, *psubtype = NULL, *pfile = NULL;
10376 /*------- Figure out suggested file name ----*/
10377 if(body){
10378 if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){
10380 * if part is generic, see if we can get anything
10381 * more from the suggested filename's extension...
10383 if(body->type == TYPEAPPLICATION
10384 && (!body->subtype
10385 || !strucmp(body->subtype, "octet-stream"))){
10386 BODY *fakebody = mail_newbody();
10388 if(set_mime_type_by_extension(fakebody, pfile)){
10389 ptype = body_type_names(fakebody->type);
10390 psubtype = cpystr(fakebody->subtype);
10393 mail_free_body(&fakebody);
10397 if(!ptype) {
10398 ptype = body_type_names(body->type);
10399 psubtype = cpystr(body->subtype
10400 ? body->subtype
10401 : (body->type == TYPETEXT)
10402 ? "plain"
10403 : (body->type == TYPEAPPLICATION)
10404 ? "octet-stream"
10405 : "");
10408 else{
10409 ptype = body_type_names(TYPETEXT);
10410 psubtype = cpystr("plain");
10413 if(extObjp){
10414 *extObjp = Tcl_NewStringObj("", 0);
10416 if(ptype && psubtype && pfile){
10417 size_t l;
10418 char *mtype;
10419 char extbuf[32]; /* mailcap.c limits to three */
10421 l = strlen(ptype) + strlen(psubtype) + 1;
10422 mtype = (char *) fs_get((l+1) * sizeof(char));
10424 snprintf(mtype, l+1, "%s/%s", ptype, psubtype);
10426 if(!set_mime_extension_by_type(extbuf, mtype)){
10427 char *dotp, *p;
10429 for(dotp = NULL, p = pfile; *p; p++)
10430 if(*p == '.')
10431 dotp = p + 1;
10433 if(dotp)
10434 Tcl_SetStringObj(*extObjp, dotp, -1);
10436 else
10437 Tcl_SetStringObj(*extObjp, extbuf, -1);
10439 fs_give((void **) &mtype);
10443 if(tObjp)
10444 *tObjp = Tcl_NewStringObj(ptype, -1);
10446 if(psubtype){
10447 if(stObjp)
10448 *stObjp = Tcl_NewStringObj(psubtype, -1);
10450 fs_give((void **) &psubtype);
10452 else if(stObjp)
10453 *stObjp = Tcl_NewStringObj("", 0);
10455 if(pfile){
10456 if(fnObjp)
10457 *fnObjp = Tcl_NewStringObj(pfile, -1);
10459 fs_give((void **) &pfile);
10461 else if(fnObjp)
10462 *fnObjp = Tcl_NewStringObj("", 0);
10467 * peAppListF - generate a list of elements based on fmt string,
10468 * then append it to the given list object
10472 peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...)
10474 va_list args;
10475 char *p, *sval, nbuf[128];
10476 int ival, err = 0;
10477 unsigned int uval;
10478 long lval;
10479 unsigned long luval;
10480 PATTERN_S *pval;
10481 ADDRESS *aval;
10482 INTVL_S *vval;
10483 Tcl_Obj *lObj = NULL, *sObj;
10485 if((lObj = Tcl_NewListObj(0, NULL)) != NULL){
10486 va_start(args, fmt);
10487 for(p = fmt; *p && !err; p++){
10488 sObj = NULL;
10490 if(*p == '%')
10491 switch(*++p){
10492 case 'i' : /* int value */
10493 ival = va_arg(args, int);
10494 if((sObj = Tcl_NewIntObj(ival)) == NULL)
10495 err++;
10497 break;
10499 case 'u' : /* unsigned int value */
10500 uval = va_arg(args, unsigned int);
10501 snprintf(nbuf, sizeof(nbuf), "%u", uval);
10502 if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10503 err++;
10505 break;
10507 case 'l' : /* long value */
10508 if(*(p+1) == 'u'){
10509 p++;
10510 luval = va_arg(args, unsigned long);
10511 snprintf(nbuf, sizeof(nbuf), "%lu", luval);
10512 if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10513 err++;
10515 else{
10516 lval = va_arg(args, long);
10517 if((sObj = Tcl_NewLongObj(lval)) == NULL)
10518 err++;
10521 break;
10523 case 's' : /* string value */
10524 sval = va_arg(args, char *);
10525 sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10526 if(sObj == NULL)
10527 err++;
10529 break;
10531 case 'a': /* ADDRESS list */
10532 aval = va_arg(args, ADDRESS *);
10533 if(aval){
10534 char *tmp, *p;
10535 RFC822BUFFER rbuf;
10536 size_t len;
10538 len = est_size(aval);
10539 tmp = (char *) fs_get(len * sizeof(char));
10540 tmp[0] = '\0';
10541 rbuf.f = dummy_soutr;
10542 rbuf.s = NULL;
10543 rbuf.beg = tmp;
10544 rbuf.cur = tmp;
10545 rbuf.end = tmp+len-1;
10546 rfc822_output_address_list(&rbuf, aval, 0L, NULL);
10547 *rbuf.cur = '\0';
10548 p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
10549 sObj = Tcl_NewStringObj(p, strlen(p));
10550 fs_give((void **) &tmp);
10552 else
10553 sObj = Tcl_NewStringObj("", -1);
10555 break;
10557 case 'p': /* PATTERN_S * */
10558 pval = va_arg(args, PATTERN_S *);
10559 sval = pattern_to_string(pval);
10560 sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10561 break;
10563 case 'v': /* INTVL_S * */
10564 vval = va_arg(args, INTVL_S *);
10565 if(vval){
10566 for(; vval != NULL; vval = vval->next){
10567 peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax);
10570 else
10571 sObj = Tcl_NewListObj(0, NULL);
10573 break;
10575 case 'o': /* Tcl_Obj * */
10576 sObj = va_arg(args, Tcl_Obj *);
10577 break;
10580 if(sObj)
10581 Tcl_ListObjAppendElement(interp, lObj, sObj);
10584 va_end(args);
10587 return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR);
10591 * pePatAppendID - append list of pattern identity variables to given object
10593 void
10594 pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10596 Tcl_Obj *resObj;
10598 resObj = Tcl_NewListObj(0, NULL);
10599 peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick);
10600 peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment);
10601 Tcl_ListObjAppendElement(interp, patObj, resObj);
10606 * pePatAppendPattern - append list of pattern variables to given object
10608 void
10609 pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10611 ARBHDR_S *ah;
10612 Tcl_Obj *resObj;
10614 resObj = Tcl_NewListObj(0, NULL);
10615 peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to);
10616 peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from);
10617 peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender);
10618 peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc);
10619 peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip);
10620 peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic);
10621 peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news);
10622 peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj);
10623 peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext);
10624 peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext);
10625 peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword);
10626 peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets);
10628 peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score);
10629 peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age);
10630 peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size);
10632 if((ah = pat->patgrp->arbhdr) != NULL){
10633 Tcl_Obj *hlObj, *hObj;
10635 hlObj = Tcl_NewListObj(0, NULL);
10636 Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1));
10638 for(; ah; ah = ah->next){
10639 hObj = Tcl_NewListObj(0, NULL);
10640 peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p);
10641 Tcl_ListObjAppendElement(interp, hlObj, hObj);
10644 Tcl_ListObjAppendElement(interp, resObj, hlObj);
10647 switch(pat->patgrp->fldr_type){
10648 case FLDR_ANY:
10649 peAppListF(interp, resObj, "%s%s", "ftype", "any");
10650 break;
10651 case FLDR_NEWS:
10652 peAppListF(interp, resObj, "%s%s", "ftype", "news");
10653 break;
10654 case FLDR_EMAIL:
10655 peAppListF(interp, resObj, "%s%s", "ftype", "email");
10656 break;
10657 case FLDR_SPECIFIC:
10658 peAppListF(interp, resObj, "%s%s", "ftype", "specific");
10659 break;
10662 peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder);
10663 peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new));
10664 peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec));
10665 peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del));
10666 peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp));
10667 peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans));
10668 peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj));
10669 peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom));
10670 peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy));
10672 Tcl_ListObjAppendElement(interp, patObj, resObj);
10676 char *
10677 pePatStatStr(int value)
10679 switch(value){
10680 case PAT_STAT_EITHER:
10681 return("either");
10682 break;
10684 case PAT_STAT_YES:
10685 return("yes");
10686 break;
10688 default :
10689 return("no");
10690 break;
10696 * peCreateUserContext - create new ps_global and set it up
10698 char *
10699 peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf)
10701 if(ps_global)
10702 peDestroyUserContext(&ps_global);
10704 set_collation(1, 1);
10706 ps_global = new_pine_struct();
10708 /*----------------------------------------------------------------------
10709 Place any necessary constraints on pith processing
10710 ----------------------------------------------------------------------*/
10712 /* got thru close procedure without expunging */
10713 ps_global->noexpunge_on_close = 1;
10715 /* do NOT let user set path to local executable */
10716 ps_global->vars[V_SENDMAIL_PATH].is_user = 0;
10719 /*----------------------------------------------------------------------
10720 Proceed with reading acquiring user settings
10721 ----------------------------------------------------------------------*/
10722 if(ps_global->pinerc)
10723 fs_give((void **) &ps_global->pinerc);
10725 if(ps_global->prc)
10726 free_pinerc_s(&ps_global->prc);
10728 if(!IS_REMOTE(config)){
10729 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config);
10730 return(tmp_20k_buf);
10733 ps_global->prc = new_pinerc_s(config);
10735 if(defconf){
10736 if(ps_global->pconf)
10737 free_pinerc_s(&ps_global->pconf);
10739 ps_global->pconf = new_pinerc_s(defconf);
10743 * Fake up some user information
10745 ps_global->ui.login = cpystr(user);
10747 #ifdef DEBUG
10749 * Prep for IMAP debugging
10751 setup_imap_debug();
10752 #endif
10754 /* CHECK FOR AND PASS BACK ANY INIT ERRORS */
10755 return(peLoadConfig(ps_global));
10760 void
10761 peDestroyUserContext(struct pine **pps)
10764 completely_done_with_adrbks();
10766 free_pinerc_strings(pps);
10767 #if 0
10768 imap_flush_passwd_cache(TRUE);
10769 #endif
10770 clear_index_cache(sp_inbox_stream(), 0);
10771 free_newsgrp_cache();
10772 mailcap_free();
10773 close_patterns(0L);
10774 free_extra_hdrs();
10775 free_contexts(&ps_global->context_list);
10777 pico_endcolor();
10779 free_strlist(&peCertHosts);
10781 free_pine_struct(pps);
10785 char *
10786 peLoadConfig(struct pine *pine_state)
10788 int rv;
10789 char *s, *db = NULL;
10790 extern void init_signals(void); /* in signal.c */
10792 if(!pine_state)
10793 return("No global state present");
10795 #if 0
10796 /*turned off because we don't care about local user*/
10797 /* need home directory early */
10798 get_user_info(&pine_state->ui);
10800 pine_state->home_dir = cpystr((getenv("HOME") != NULL)
10801 ? getenv("HOME")
10802 : pine_state->ui.homedir);
10803 #endif
10805 init_pinerc(pine_state, &db);
10807 fs_give((void **) &db);
10810 * Initial allocation of array of stream pool pointers.
10811 * We do this before init_vars so that we can re-use streams used for
10812 * remote config files. These sizes may get changed later.
10814 ps_global->s_pool.max_remstream = 2;
10816 ps_global->c_client_error[0] = '\0';
10818 pePrepareForAuthException();
10820 peInitVars(pine_state);
10822 if((s = peAuthException()) != NULL)
10823 return(s);
10824 else if(ps_global->c_client_error[0])
10825 return(ps_global->c_client_error);
10827 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
10828 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
10829 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
10832 * Install callback to handle certificate validation failures,
10833 * allowing the user to continue if they wish.
10835 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery);
10836 mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure);
10839 * Set up a c-client read timeout and timeout handler. In general,
10840 * it shouldn't happen, but a server crash or dead link can cause
10841 * pine to appear wedged if we don't set this up...
10843 mail_parameters(NULL, SET_OPENTIMEOUT,
10844 (void *)((pine_state->VAR_TCPOPENTIMEO
10845 && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
10846 ? (long) rv : 30L));
10847 mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout);
10849 if(pine_state->VAR_RSHOPENTIMEO
10850 && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
10851 mail_parameters(NULL, SET_RSHTIMEOUT, (void *) (long) rv);
10853 if(pine_state->VAR_SSHOPENTIMEO
10854 && ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4))
10855 mail_parameters(NULL, SET_SSHTIMEOUT, (void *) (long) rv);
10858 * Tell c-client not to be so aggressive about uid mappings
10860 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
10863 * Setup referral handling
10865 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
10866 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
10869 * Install extra headers to fetch along with all the other stuff
10870 * mail_fetch_structure and mail_fetch_overview requests.
10872 calc_extra_hdrs();
10874 if(get_extra_hdrs())
10875 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs());
10877 (void) init_username(pine_state);
10879 (void) init_hostname(ps_global);
10881 #ifdef ENABLE_LDAP
10882 (void) init_ldap_pname(ps_global);
10883 #endif /* ENABLE_LDAP */
10885 if(ps_global->prc && ps_global->prc->type == Loc &&
10886 can_access(ps_global->pinerc, ACCESS_EXISTS) == 0 &&
10887 can_access(ps_global->pinerc, EDIT_ACCESS) != 0)
10888 ps_global->readonly_pinerc = 1;
10891 * c-client needs USR2 and we might as well
10892 * do something sensible with HUP and TERM
10894 init_signals();
10896 strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name));
10897 pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0';
10899 init_folders(pine_state); /* digest folder spec's */
10902 * Various options we want to make sure are set OUR way
10904 F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state);
10905 F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state);
10906 F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state);
10909 * Fake screen dimensions for index formatting and
10910 * message display wrap...
10912 ps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo));
10913 ps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH;
10914 ps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH;
10915 if(ps_global->VAR_WP_COLUMNS){
10916 int w = atoi(ps_global->VAR_WP_COLUMNS);
10917 if(w >= 20 && w <= 128)
10918 ps_global->ttyo->screen_cols = w;
10921 ps_global->ttyo->header_rows = 0;
10922 ps_global->ttyo->footer_rows = 0;
10925 /* init colors */
10926 if(ps_global->VAR_NORM_FORE_COLOR)
10927 pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR);
10929 if(ps_global->VAR_NORM_BACK_COLOR)
10930 pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR);
10932 if(ps_global->VAR_REV_FORE_COLOR)
10933 pico_rfcolor(ps_global->VAR_REV_FORE_COLOR);
10935 if(ps_global->VAR_REV_BACK_COLOR)
10936 pico_rbcolor(ps_global->VAR_REV_BACK_COLOR);
10938 pico_set_normal_color();
10940 return(NULL);
10945 peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox)
10947 unsigned long flgs = 0L;
10948 char *s;
10950 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
10952 pePrepareForAuthException();
10954 if(do_inbox)
10955 flgs |= DB_INBOXWOCNTXT;
10957 if(do_broach_folder(mailbox, context, NULL, flgs) && ps_global->mail_stream){
10958 dprint((SYSDBG_INFO, "Mailbox open: %s",
10959 ps_global->mail_stream->mailbox ? ps_global->mail_stream->mailbox : "<UNKNOWN>"));
10960 return(TCL_OK);
10963 Tcl_SetResult(interp,
10964 (s = peAuthException())
10966 : (*ps_global->last_error)
10967 ? ps_global->last_error
10968 : "Login Error",
10969 TCL_VOLATILE);
10970 return(TCL_ERROR);
10974 void
10975 peDestroyStream(struct pine *ps)
10977 int cur_is_inbox;
10979 if(ps){
10980 cur_is_inbox = (sp_inbox_stream() == ps_global->mail_stream);
10982 /* clean up open streams */
10983 if(ps->mail_stream){
10984 expunge_and_close(ps->mail_stream, NULL, EC_NONE);
10985 ps_global->mail_stream = NULL;
10986 ps_global->cur_folder[0] = '\0';
10989 if(ps->msgmap)
10990 mn_give(&ps->msgmap);
10992 if(sp_inbox_stream() && !cur_is_inbox){
10993 ps->mail_stream = sp_inbox_stream();
10994 ps->msgmap = sp_msgmap(ps->mail_stream);
10995 sp_set_expunge_count(ps_global->mail_stream, 0L);
10996 expunge_and_close(sp_inbox_stream(), NULL, EC_NONE);
10997 mn_give(&ps->msgmap);
11004 * pePrepareForAuthException - set globals to get feedback from bowels of c-client
11006 void
11007 pePrepareForAuthException(void)
11009 peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0;
11013 * pePrepareForAuthException - check globals getting feedback from bowels of c-client
11015 char *
11016 peAuthException()
11018 static char buf[CRED_REQ_SIZE];
11020 if(peCertQuery){
11021 snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor);
11022 return(buf);
11025 if(peCertFailure){
11026 snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor);
11027 return(buf);
11030 if(peNoPassword){
11031 snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor);
11032 return(buf);
11035 if(peCredentialError){
11036 snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor);
11037 return(buf);
11040 return(NULL);
11044 void
11045 peInitVars(struct pine *ps)
11047 init_vars(ps, NULL);
11050 * fix display/keyboard-character-set to utf-8
11055 if(ps->display_charmap)
11056 fs_give((void **) &ps->display_charmap);
11058 ps->display_charmap = cpystr(WP_INTERNAL_CHARSET);
11060 if(ps->keyboard_charmap)
11061 fs_give((void **) &ps->keyboard_charmap);
11063 ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET);
11065 (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);;
11071 peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
11073 char *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1];
11074 long rawno;
11075 ENVELOPE *env, *outgoing = NULL;
11076 METAENV *metaenv;
11077 PINEFIELD *custom;
11078 BODY *body = NULL;
11080 if(uid){
11081 rawno = peSequenceNumber(uid);
11083 if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){
11084 if(objc == 2 && objv[1]){
11085 subj = Tcl_GetStringFromObj(objv[1], NULL);
11087 else if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) != NULL){
11088 subj = env->subject;
11090 else{
11091 Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
11092 return(TCL_ERROR);
11095 if((errstr = bounce_msg_body(ps_global->mail_stream, rawno, NULL,
11096 &to, subj, &outgoing, &body, NULL))){
11097 Tcl_SetResult(interp, errstr, TCL_VOLATILE);
11098 return(TCL_ERROR);
11101 metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11103 if(!outgoing->from)
11104 outgoing->from = generate_from();
11106 rfc822_date(tmp_20k_buf);
11107 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
11109 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11110 if(!outgoing->message_id)
11111 outgoing->message_id = generate_message_id();
11113 /* NO FCC */
11115 if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11116 errstr = errbuf;
11118 pine_free_body(&body);
11120 pine_free_env(&metaenv);
11122 if(custom)
11123 free_customs(custom);
11125 mail_free_envelope(&outgoing);
11126 pine_free_body(&body);
11131 Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE);
11132 return(errstr ? TCL_ERROR : TCL_OK);
11137 peMessageSpamNotice(interp, uid, objc, objv)
11138 Tcl_Interp *interp;
11139 imapuid_t uid;
11140 int objc;
11141 Tcl_Obj **objv;
11143 char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
11144 long rawno;
11146 if(uid){
11147 rawno = peSequenceNumber(uid);
11149 if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){
11151 if(objv[1])
11152 subj = Tcl_GetStringFromObj(objv[1], NULL);
11154 rs = peSendSpamReport(rawno, to, subj, errbuf);
11158 Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE);
11159 return(rs ? TCL_ERROR : TCL_OK);
11163 char *
11164 peSendSpamReport(long rawno, char *to, char *subj, char *errbuf)
11166 char *errstr = NULL, *tmp_a_string;
11167 ENVELOPE *env, *outgoing;
11168 METAENV *metaenv;
11169 PINEFIELD *custom;
11170 BODY *body;
11171 static char *fakedomain = "@";
11172 void *msgtext;
11175 if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) == NULL){
11176 return(ps_global->last_error);
11179 /* empty subject gets "spam" subject */
11180 if(!(subj && *subj))
11181 subj = env->subject;
11183 /*---- New Body to start with ----*/
11184 body = mail_newbody();
11185 body->type = TYPEMULTIPART;
11187 /*---- The TEXT part/body ----*/
11188 body->nested.part = mail_newbody_part();
11189 body->nested.part->body.type = TYPETEXT;
11191 if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
11192 pine_free_body(&body);
11193 return("peSendSpamReport: Can't allocate text");
11195 else{
11196 sprintf(tmp_20k_buf,
11197 "The attached message is being reported to <%s> as Spam\n",
11198 to);
11199 so_puts((STORE_S *) msgtext, tmp_20k_buf);
11200 body->nested.part->body.contents.text.data = msgtext;
11203 /*---- Attach the raw message ----*/
11204 if(forward_mime_msg(ps_global->mail_stream, rawno, NULL, env,
11205 &(body->nested.part->next), msgtext)){
11206 outgoing = mail_newenvelope();
11207 metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11209 else{
11210 pine_free_body(&body);
11211 return("peSendSpamReport: Can't generate forwarded message");
11214 /* rfc822_parse_adrlist feels free to destroy input so copy */
11215 tmp_a_string = cpystr(to);
11216 rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
11217 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
11218 ? fakedomain : ps_global->maildomain);
11219 fs_give((void **) &tmp_a_string);
11221 outgoing->from = generate_from();
11222 outgoing->subject = cpystr(subj);
11223 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11224 outgoing->message_id = generate_message_id();
11226 rfc822_date(tmp_20k_buf);
11227 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
11229 /* NO FCC for Spam Reporting */
11231 if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11232 errstr = errbuf;
11234 pine_free_body(&body);
11236 pine_free_env(&metaenv);
11238 if(custom)
11239 free_customs(custom);
11241 mail_free_envelope(&outgoing);
11242 pine_free_body(&body);
11244 return(errstr);
11248 /* * * * * * * * * * * * * Start of Composer Routines * * * * * * * * * * * */
11252 * PEComposeCmd - export various bits of alpine state
11255 PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11257 char *err = "PECompose: Unknown request";
11259 dprint((2, "PEComposeCmd"));
11261 if(objc == 1){
11262 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11264 else if(!ps_global){
11265 Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC);
11266 return(TCL_ERROR);
11268 else{
11269 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11271 if(s1){
11272 if(!strcmp(s1, "post")){
11273 long flags = PMC_NONE;
11274 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
11275 flags |= PMC_FORCE_QUAL;
11277 return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags));
11279 else if(objc == 2){
11280 if(!strcmp(s1, "userhdrs")){
11281 int i;
11282 char *p;
11283 PINEFIELD *custom, *cp;
11284 ADDRESS *from;
11285 static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL};
11287 custom = peCustomHdrs();
11289 for(i = 0; standard[i]; i++){
11290 p = NULL;
11291 for(cp = custom; cp; cp = cp->next)
11292 if(!strucmp(cp->name, standard[i]))
11293 p = cp->textbuf;
11295 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p);
11298 for(cp = custom; cp != NULL; cp = cp->next){
11299 if(!strucmp(cp->name, "from")){
11300 if(F_OFF(F_ALLOW_CHANGING_FROM, ps_global))
11301 continue;
11303 if(cp->textbuf && strlen(cp->textbuf)){
11304 p = cp->textbuf;
11306 else{
11307 RFC822BUFFER rbuf;
11309 tmp_20k_buf[0] = '\0';
11310 rbuf.f = dummy_soutr;
11311 rbuf.s = NULL;
11312 rbuf.beg = tmp_20k_buf;
11313 rbuf.cur = tmp_20k_buf;
11314 rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
11315 rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL);
11316 *rbuf.cur = '\0';
11317 mail_free_address(&from);
11318 p = tmp_20k_buf;
11321 else{
11322 p = cp->textbuf;
11323 for(i = 0; standard[i]; i++)
11324 if(!strucmp(standard[i], cp->name))
11325 p = NULL;
11327 if(!p)
11328 continue;
11331 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p);
11334 if(custom)
11335 free_customs(custom);
11337 return(TCL_OK);
11339 else if(!strcmp(s1, "syshdrs")){
11340 int i;
11341 static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL};
11343 for(i = 0; extras[i]; i++)
11344 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL);
11346 return(TCL_OK);
11348 else if(!strcmp(s1, "composehdrs")){
11349 char **p, *q;
11351 if((p = ps_global->VAR_COMP_HDRS) && *p){
11352 for(; *p; p++)
11353 Tcl_ListObjAppendElement(interp,
11354 Tcl_GetObjResult(interp),
11355 Tcl_NewStringObj(*p, (q = strchr(*p, ':'))
11356 ? (q - *p) : -1));
11358 else
11359 Tcl_SetResult(interp, "", TCL_STATIC);
11361 return(TCL_OK);
11363 else if(!strcmp(s1, "fccdefault")){
11364 int ci = 0;
11365 CONTEXT_S *c = default_save_context(ps_global->context_list), *c2;
11367 for(c2 = ps_global->context_list; c && c != c2; c2 = c2->next)
11368 ci++;
11370 Tcl_ListObjAppendElement(interp,
11371 Tcl_GetObjResult(interp),
11372 Tcl_NewIntObj(ci));
11373 Tcl_ListObjAppendElement(interp,
11374 Tcl_GetObjResult(interp),
11375 Tcl_NewStringObj(ps_global->VAR_DEFAULT_FCC
11376 ? ps_global->VAR_DEFAULT_FCC
11377 : "", -1));
11378 return(TCL_OK);
11380 else if(!strcmp(s1, "noattach")){
11381 peFreeAttach(&peCompAttach);
11382 Tcl_SetResult(interp, "OK", TCL_VOLATILE);
11383 return(TCL_OK);
11385 else if(!strcmp(s1, "from")){
11386 RFC822BUFFER rbuf;
11387 ADDRESS *from = generate_from();
11388 tmp_20k_buf[0] = '\0';
11389 rbuf.f = dummy_soutr;
11390 rbuf.s = NULL;
11391 rbuf.beg = tmp_20k_buf;
11392 rbuf.cur = tmp_20k_buf;
11393 rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
11394 rfc822_output_address_list(&rbuf, from, 0L, NULL);
11395 *rbuf.cur = '\0';
11396 mail_free_address(&from);
11397 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
11398 return(TCL_OK);
11400 else if(!strcmp(s1, "attachments")){
11401 COMPATT_S *p;
11402 Tcl_Obj *objAttach;
11404 for(p = peCompAttach; p; p = p->next)
11405 if(p->file){
11406 objAttach = Tcl_NewListObj(0, NULL);
11408 /* id */
11409 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11411 /* file name */
11412 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1));
11414 /* file size */
11415 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size));
11417 /* type/subtype */
11418 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype);
11419 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf,-1));
11421 /* append to list */
11422 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11424 else if(p->body){
11425 char *name;
11427 objAttach = Tcl_NewListObj(0, NULL);
11429 /* id */
11430 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11432 /* file name */
11433 if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){
11434 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1));
11435 fs_give((void **) &name);
11437 else
11438 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1));
11440 /* file size */
11441 Tcl_ListObjAppendElement(interp, objAttach,
11442 Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64)
11443 ? ((p->l.b.body->size.bytes * 3)/4)
11444 : p->l.b.body->size.bytes));
11446 /* type/subtype */
11447 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11448 body_type_names(p->l.b.body->type),
11449 p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown");
11450 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf, -1));
11452 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11455 return(TCL_OK);
11458 else if(objc == 3){
11459 if(!strcmp(s1, "unattach")){
11460 char *id;
11462 if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11463 if(peClearAttachID(id)){
11464 Tcl_SetResult(interp, "OK", TCL_STATIC);
11465 return(TCL_OK);
11467 else
11468 err = "Can't access attachment id";
11470 else
11471 err = "Can't read attachment id";
11473 else if(!strcmp(s1, "attachinfo")){
11474 COMPATT_S *a;
11475 char *id, *s;
11477 /* return: remote-filename size "type/subtype" */
11478 if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11479 if((a = peGetAttachID(id)) != NULL){
11480 if(a->file){
11481 /* file name */
11482 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1));
11484 /* file size */
11485 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size));
11487 /* type/subtype */
11488 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype);
11489 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf,-1));
11491 /* description */
11492 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11493 Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1));
11494 return(TCL_OK);
11496 else if(a->body){
11497 char *name;
11499 /* file name */
11500 if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){
11501 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1));
11502 fs_give((void **) &name);
11504 else
11505 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1));
11507 /* file size */
11508 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11509 Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64)
11510 ? ((a->l.b.body->size.bytes * 3)/4)
11511 : a->l.b.body->size.bytes));
11513 /* type/subtype */
11514 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11515 body_type_names(a->l.b.body->type),
11516 a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown");
11518 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
11520 /* description */
11521 if(a->l.b.body->description){
11522 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description);
11524 else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){
11525 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s);
11526 fs_give((void **) &s);
11528 else
11529 tmp_20k_buf[0] = '\0';
11531 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
11533 return(TCL_OK);
11536 err = "Unknown attachment type";
11538 else
11539 err = "Can't access attachment id";
11541 else
11542 err = "Can't read attachment id";
11545 else if(objc == 7){
11546 if(!strcmp(s1, "attach")){
11547 char *file, *remote, *type, *subtype, *desc;
11549 if((file = Tcl_GetStringFromObj(objv[2], NULL))
11550 && (type = Tcl_GetStringFromObj(objv[3], NULL))
11551 && (subtype = Tcl_GetStringFromObj(objv[4], NULL))
11552 && (remote = Tcl_GetStringFromObj(objv[5], NULL))){
11553 int dl;
11555 desc = Tcl_GetStringFromObj(objv[6], &dl);
11557 if(desc){
11558 Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE);
11559 return(TCL_OK);
11561 else
11562 err = "Can't read file description";
11564 else
11565 err = "Can't read file name";
11571 Tcl_SetResult(interp, err, TCL_STATIC);
11572 return(TCL_ERROR);
11576 COMPATT_S *
11577 peNewAttach(void)
11579 COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S));
11580 memset(p, 0, sizeof(COMPATT_S));
11581 return(p);
11585 void
11586 peFreeAttach(COMPATT_S **a)
11588 if(a && *a){
11589 fs_give((void **) &(*a)->id);
11591 if((*a)->file){
11592 if((*a)->l.f.type)
11593 fs_give((void **) &(*a)->l.f.type);
11595 if((*a)->l.f.subtype)
11596 fs_give((void **) &(*a)->l.f.subtype);
11598 if((*a)->l.f.remote)
11599 fs_give((void **) &(*a)->l.f.remote);
11601 if((*a)->l.f.local){
11602 (void) unlink((*a)->l.f.local);
11603 fs_give((void **) &(*a)->l.f.local);
11606 if((*a)->l.f.description)
11607 fs_give((void **) &(*a)->l.f.description);
11609 else if((*a)->body){
11610 pine_free_body(&(*a)->l.b.body);
11613 peFreeAttach(&(*a)->next);
11614 fs_give((void **) a);
11619 char *
11620 peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl)
11622 COMPATT_S *ap = peNewAttach(), *p;
11623 long hval;
11625 ap->file = TRUE;
11626 ap->l.f.local = cpystr(f);
11627 ap->l.f.size = name_file_size(f);
11629 hval = line_hash(f);
11630 while(1) /* collisions? */
11631 if(peGetAttachID(ap->id = cpystr(long2string(hval)))){
11632 fs_give((void **) &ap->id);
11633 hval += 1;
11635 else
11636 break;
11638 ap->l.f.remote = cpystr(r ? r : "");
11639 ap->l.f.type = cpystr(t ? t : "Text");
11640 ap->l.f.subtype = cpystr(st ? st : "Plain");
11642 ap->l.f.description = fs_get(dl + 1);
11643 snprintf(ap->l.f.description, dl + 1, "%s", d);
11645 if((p = peCompAttach) != NULL){
11647 if(!p->next){
11648 p->next = ap;
11649 break;
11651 while((p = p->next) != NULL);
11653 else
11654 peCompAttach = ap;
11656 return(ap->id);
11660 char *
11661 peBodyAttachID(BODY *b)
11663 COMPATT_S *ap = peNewAttach(), *p;
11664 unsigned long hval;
11666 ap->body = TRUE;
11667 ap->l.b.body = copy_body(NULL, b);
11669 hval = b->id ? line_hash(b->id) : time(0);
11670 while(1) /* collisions? */
11671 if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){
11672 fs_give((void **) &ap->id);
11673 hval += 1;
11675 else
11676 break;
11678 /* move contents pointer to copy */
11679 peBodyMoveContents(b, ap->l.b.body);
11681 if((p = peCompAttach) != NULL){
11683 if(!p->next){
11684 p->next = ap;
11685 break;
11687 while((p = p->next) != NULL);
11689 else
11690 peCompAttach = ap;
11692 return(ap->id);
11696 void
11697 peBodyMoveContents(BODY *bs, BODY *bd)
11699 if(bs && bd){
11700 if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){
11701 PART *ps = bs->nested.part,
11702 *pd = bd->nested.part;
11703 do /* for each part */
11704 peBodyMoveContents(&ps->body, &pd->body);
11705 while ((ps = ps->next) && (pd = pd->next)); /* until done */
11707 else if(bs->contents.text.data){
11708 bd->contents.text.data = bs->contents.text.data;
11709 bs->contents.text.data = NULL;
11716 COMPATT_S *
11717 peGetAttachID(char *h)
11719 COMPATT_S *p;
11721 for(p = peCompAttach; p; p = p->next)
11722 if(!strcmp(p->id, h))
11723 return(p);
11725 return(NULL);
11730 peClearAttachID(char *h)
11732 COMPATT_S *pp, *pt = NULL;
11734 for(pp = peCompAttach; pp; pp = pp->next){
11735 if(!strcmp(pp->id, h)){
11736 if(pt)
11737 pt->next = pp->next;
11738 else
11739 peCompAttach = pp->next;
11741 pp->next = NULL;
11742 peFreeAttach(&pp);
11743 return(TRUE);
11746 pt = pp;
11749 return(FALSE);
11754 * peDoPost - handle preparing header and body text for posting, prepare
11755 * for any Fcc, then call the mailer to send it out
11758 peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
11760 int rv = TCL_OK, recipients;
11761 char *s;
11763 if(commence_fcc(fcc, fcc_cntxtp, TRUE)){
11765 ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
11766 pePrepareForAuthException();
11768 if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc))
11769 && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){
11770 if((s = peAuthException()) != NULL){
11771 strcpy(errp, s);
11773 else if(ps_global->last_error[0]){
11774 sprintf(errp, "Send Error: %.*s", 64, ps_global->last_error);
11776 else if(ps_global->c_client_error[0]){
11777 sprintf(errp, "Send Error: %.*s", 64, ps_global->c_client_error);
11779 else
11780 strcpy(errp, "Sending Failure");
11782 rv = TCL_ERROR;
11783 dprint((1, "call_mailer failed!"));
11785 else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){
11786 strcpy(errp, "Fcc Failed!. No message saved.");
11787 rv = TCL_ERROR;
11788 dprint((1, "explicit fcc write failed!"));
11790 else{
11791 PINEFIELD *pf;
11792 REPLY_S *reply = NULL;
11794 /* success, now look for x-reply-uid to flip answered flag for? */
11796 for(pf = metaenv->local; pf && pf->name; pf = pf->next)
11797 if(!strucmp(pf->name, "x-reply-uid")){
11798 if(pf->textbuf){
11799 if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){
11801 update_answered_flags(reply);
11803 if(reply->mailbox)
11804 fs_give((void **) &reply->mailbox);
11806 if(reply->prefix)
11807 fs_give((void **) &reply->prefix);
11809 if(reply->data.uid.msgs)
11810 fs_give((void **) &reply->data.uid.msgs);
11812 fs_give((void **) &reply);
11816 break;
11820 else{
11821 dprint((1,"can't open fcc, cont"));
11823 strcpy(errp, "Can't open Fcc");
11824 rv = TCL_ERROR;
11827 return(rv);
11833 * pePostponeCmd - export various bits of alpine state
11836 PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11838 char *err = "PEPostpone: unknown request";
11839 long uidl;
11840 imapuid_t uid;
11842 dprint((2, "PEPostponeCmd"));
11844 if(objc == 1){
11845 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11847 else if(!ps_global){
11848 Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC);
11849 return(TCL_ERROR);
11851 else{
11852 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11854 if(s1){
11855 if(!strcmp(s1, "extract")){
11856 if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){
11857 Tcl_Obj *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL;
11858 MAILSTREAM *stream;
11859 BODY *b;
11860 ENVELOPE *env = NULL;
11861 PINEFIELD *custom = NULL, *cp;
11862 REPLY_S *reply = NULL;
11863 ACTION_S *role = NULL;
11864 STORE_S *so;
11865 long n;
11866 int rv = TCL_OK;
11867 char *fcc = NULL, *lcc = NULL;
11868 unsigned flags = REDRAFT_DEL | REDRAFT_PPND;
11870 uid = uidl;
11871 if(objc > 3){ /* optional flags */
11872 int i, nFlags;
11873 Tcl_Obj **objFlags;
11874 char *flagstr;
11876 Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags);
11877 for(i = 0; i < nFlags; i++){
11878 if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
11879 rv = TCL_ERROR;
11882 if(!strucmp(flagstr, "html"))
11883 flags |= REDRAFT_HTML;
11886 /* BUG: should probably complain if argc > 4 */
11888 if(rv == TCL_OK
11889 && postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)
11890 && stream){
11891 if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
11892 if((n = mail_msgno(stream, uid)) > 0L){
11893 if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply,
11894 NULL, &custom, &role, /* should role be NULL? */
11895 flags, so)){
11896 char *charset = NULL;
11898 /* prepare to package up for caller */
11899 objHdr = Tcl_NewListObj(0, NULL);
11901 /* determine body part's charset */
11902 if((charset = parameter_val(b->parameter,"charset")) != NULL){
11903 objOpts = Tcl_NewListObj(0, NULL);
11904 peAppListF(interp, objOpts, "%s%s", "charset", charset);
11905 fs_give((void **) &charset);
11908 /* body part's MIME subtype */
11909 if(b->subtype && strucmp(b->subtype,"plain")){
11910 if(!objOpts)
11911 objOpts = Tcl_NewListObj(0, NULL);
11913 peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype);
11916 peAppListF(interp, objHdr, "%s%a", "from",
11917 role && role->from ? role->from : env->from);
11918 peAppListF(interp, objHdr, "%s%a", "to", env->to);
11919 peAppListF(interp, objHdr, "%s%a", "cc", env->cc);
11920 peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc);
11921 peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to);
11922 peAppListF(interp, objHdr, "%s%s", "subject",
11923 rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
11924 SIZEOF_20KBUF, env->subject));
11926 if(fcc)
11927 peFccAppend(interp, objHdr, fcc, -1);
11929 for(cp = custom; cp && cp->name; cp = cp->next)
11930 switch(cp->type){
11931 case Address :
11932 strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
11933 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11934 peAppListF(interp, objHdr, "%s%a",
11935 lcase((unsigned char *) tmp_20k_buf), *cp->addr);
11936 break;
11938 case Attachment :
11939 break;
11941 case Fcc :
11942 case Subject :
11943 break; /* ignored */
11945 default :
11946 strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
11947 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11948 peAppListF(interp, objHdr, "%s%s",
11949 lcase((unsigned char *) tmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val);
11950 break;
11953 if(reply){
11954 /* blat x-Reply-UID: for possible use? */
11955 if(reply->uid){
11956 char uidbuf[MAILTMPLEN], *p;
11957 long i;
11959 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
11960 if(i)
11961 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
11963 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-tmp_20k_buf));
11966 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11968 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
11969 reply->prefix ? int2string(strlen(reply->prefix))
11970 : (reply->forwarded) ? "" : "0 ",
11971 reply->prefix ? " " : "",
11972 reply->prefix ? reply->prefix : "",
11973 i, reply->data.uid.validity,
11974 tmp_20k_buf, reply->mailbox);
11976 peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf);
11979 fs_give((void **) &reply->mailbox);
11980 fs_give((void **) &reply->prefix);
11981 fs_give((void **) &reply->data.uid.msgs);
11982 fs_give((void **) &reply);
11985 objBod = Tcl_NewListObj(0, NULL);
11986 peSoStrToList(interp, objBod, so);
11988 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr);
11989 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod);
11991 objAttach = peMsgAttachCollector(interp, b);
11993 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11995 if(objOpts){
11996 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts);
11999 /* clean up */
12000 if(fcc)
12001 fs_give((void **) &fcc);
12003 if(lcc)
12004 fs_give((void **) &lcc);
12006 mail_free_envelope(&env);
12007 pine_free_body(&b);
12008 free_action(&role);
12010 /* if Drafts got whacked, open INBOX */
12011 if(!ps_global->mail_stream)
12012 do_broach_folder(ps_global->inbox_name,
12013 ps_global->context_list,
12014 NULL, DB_INBOXWOCNTXT);
12016 return(TCL_OK);
12019 so_give(&so);
12021 else
12022 err = "Unknown UID";
12024 else
12025 err = "No internal storage";
12027 /* redraft_work cleaned up the "stream" */
12029 else
12030 err = "No Postponed stream";
12032 else
12033 err = "Malformed extract request";
12035 else if(objc == 2){
12036 if(!strcmp(s1, "any")){
12037 MAILSTREAM *stream;
12039 if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12040 Tcl_SetResult(interp, "1", TCL_STATIC);
12042 if(stream != ps_global->mail_stream)
12043 pine_mail_close(stream);
12045 else
12046 Tcl_SetResult(interp, "0", TCL_STATIC);
12048 return(TCL_OK);
12050 else if(!strcmp(s1, "count")){
12051 MAILSTREAM *stream;
12053 if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12054 Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC);
12056 if(stream != ps_global->mail_stream)
12057 pine_mail_close(stream);
12059 else
12060 Tcl_SetResult(interp, "-1", TCL_STATIC);
12062 return(TCL_OK);
12064 else if(!strcmp(s1, "list")){
12065 MAILSTREAM *stream;
12066 ENVELOPE *env;
12067 Tcl_Obj *objEnv = NULL, *objEnvList;
12068 long n;
12070 if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12071 if(!stream->nmsgs){
12072 (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND);
12073 Tcl_SetResult(interp, "", TCL_STATIC);
12074 return(TCL_OK);
12077 objEnvList = Tcl_NewListObj(0, NULL);
12079 for(n = 1; n <= stream->nmsgs; n++){
12080 if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){
12081 objEnv = Tcl_NewListObj(0, NULL);
12083 peAppListF(interp, objEnv, "%s%s", "uid",
12084 ulong2string(mail_uid(stream, n)));
12086 peAppListF(interp, objEnv, "%s%a", "to", env->to);
12088 date_str((char *)env->date, iSDate, 1, tmp_20k_buf, SIZEOF_20KBUF, 0);
12090 peAppListF(interp, objEnv, "%s%s", "date", tmp_20k_buf);
12092 peAppListF(interp, objEnv, "%s%s", "subj",
12093 rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
12094 SIZEOF_20KBUF, env->subject));
12096 Tcl_ListObjAppendElement(interp, objEnvList, objEnv);
12100 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList);
12102 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
12103 Tcl_NewStringObj("utf-8", -1));
12105 if(stream != ps_global->mail_stream)
12106 pine_mail_close(stream);
12109 return(TCL_OK);
12112 else if(objc == 3){
12113 if(!strcmp(s1, "append")){
12114 int rv;
12116 if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_NONE)) == TCL_OK)
12117 Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
12119 return(rv);
12121 else if(!strcmp(s1, "draft")){
12122 int rv;
12124 if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_PRSRV_ATT)) == TCL_OK)
12125 Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
12127 return(rv);
12129 else if(!strcmp(s1, "delete")){
12130 if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){
12131 MAILSTREAM *stream;
12132 long rawno;
12134 uid = uidl;
12135 if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12136 if((rawno = mail_msgno(stream, uid)) > 0L){
12137 mail_flag(stream, long2string(rawno), "\\DELETED", ST_SET);
12138 ps_global->expunge_in_progress = 1;
12139 mail_expunge(stream);
12140 ps_global->expunge_in_progress = 0;
12141 if(stream != ps_global->mail_stream)
12142 pine_mail_actually_close(stream);
12144 return(TCL_OK);
12146 else
12147 err = "PEPostpone delete: UID no longer exists";
12149 else
12150 err = "PEPostpone delete: No Postponed stream";
12152 else
12153 err = "PEPostpone delete: No uid provided";
12159 Tcl_SetResult(interp, err, TCL_STATIC);
12160 return(TCL_ERROR);
12165 * peDoPostpone - handle postponing after message collection
12168 peDoPostpone(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
12170 PINEFIELD *pf;
12171 int rv;
12172 appenduid_t *au;
12175 * resolve fcc and store it in fcc custom header field data
12177 if(fcc && *fcc && fcc_cntxtp && *fcc_cntxtp)
12178 for(pf = metaenv->local; pf && pf->name; pf = pf->next)
12179 if(!strucmp("fcc", pf->name)){
12180 char *name, *rs, path_in_context[MAILTMPLEN];
12182 if(pf->textbuf) /* free old value */
12183 fs_give((void **) &pf->textbuf);
12185 /* replace nickname with full name */
12186 if(!(name = folder_is_nick(fcc, FOLDERS(*fcc_cntxtp), FN_NONE)))
12187 name = fcc;
12189 if(context_isambig(name) && !(((*fcc_cntxtp)->use) & CNTXT_SAVEDFLT)){
12190 context_apply(path_in_context, *fcc_cntxtp, name, sizeof(path_in_context));
12191 rs = IS_REMOTE(path_in_context) ? path_in_context : NULL;
12193 else
12194 rs = cpystr(name);
12196 if(rs){
12197 pf->textbuf = cpystr(rs);
12198 pf->text = &pf->textbuf;
12201 break;
12204 au = mail_parameters(NIL, GET_APPENDUID, NIL);
12205 mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb);
12207 rv = write_postponed(metaenv, body);
12209 mail_parameters(NIL, SET_APPENDUID, (void *) au);
12211 return((rv < 0) ? TCL_ERROR : TCL_OK);
12216 * peMsgCollector - Collect message parts and call specified handler
12219 peMsgCollector(Tcl_Interp *interp,
12220 int objc,
12221 Tcl_Obj **objv,
12222 int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *),
12223 long flags)
12225 Tcl_Obj **objMsg, **objField, **objBody;
12226 int i, j, vl, nMsg, nField, nBody;
12227 char *field, *value, *err = NULL;
12228 MSG_COL_S md;
12229 PINEFIELD *pf;
12230 STRLIST_S *tp, *lp;
12231 static char *fakedomain = "@";
12233 memset(&md, 0, sizeof(MSG_COL_S));
12234 md.postop_fcc_no_attach = -1;
12235 md.postfunc = postfunc;
12236 md.qualified_addrs = ((flags & PMC_FORCE_QUAL) == PMC_FORCE_QUAL);
12238 if(objc != 1){
12239 Tcl_SetResult(interp, "Malformed message data", TCL_STATIC);
12240 return(TCL_ERROR);
12242 else if(!ps_global){
12243 Tcl_SetResult(interp, "No open folder", TCL_STATIC);
12244 return(TCL_ERROR);
12247 md.outgoing = mail_newenvelope();
12249 md.metaenv = pine_new_env(md.outgoing, NULL, NULL, md.custom = peCustomHdrs());
12251 Tcl_ListObjGetElements(interp, objv[0], &nMsg, &objMsg);
12252 for(i = 0; i < nMsg; i++){
12253 if(Tcl_ListObjGetElements(interp, objMsg[i], &nField, &objField) != TCL_OK){
12254 err = ""; /* interp's result object has error message */
12255 return(peMsgCollected(interp, &md, err, flags));
12258 if(nField && (field = Tcl_GetStringFromObj(objField[0], NULL))){
12259 if(!strcmp(field, "body")){
12260 if(md.msgtext){
12261 err = "Too many bodies";
12262 return(peMsgCollected(interp, &md, err, flags));
12264 else if((md.msgtext = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
12265 /* mark storage object as user edited */
12266 (void) so_attr(md.msgtext, "edited", "1");
12268 Tcl_ListObjGetElements(interp, objField[1], &nBody, &objBody);
12269 for(j = 0; j < nBody; j++){
12270 value = Tcl_GetStringFromObj(objBody[j], &vl);
12271 if(value){
12272 so_nputs(md.msgtext, value, vl);
12273 so_puts(md.msgtext, "\n");
12275 else{
12276 err = "Value read failure";
12277 return(peMsgCollected(interp, &md, err, flags));
12281 else {
12282 err = "Can't acquire body storage";
12283 return(peMsgCollected(interp, &md, err, flags));
12286 else if(!strucmp(field, "attach")){
12287 char *id;
12288 COMPATT_S *a;
12290 if(nField == 2
12291 && (id = Tcl_GetStringFromObj(objField[1], NULL))
12292 && (a = peGetAttachID(id))){
12293 tp = new_strlist(id);
12294 if((lp = md.attach) != NULL){
12296 if(!lp->next){
12297 lp->next = tp;
12298 break;
12300 while((lp = lp->next) != NULL);
12302 else
12303 md.attach = tp;
12305 else{
12306 strcpy(err = tmp_20k_buf, "Unknown attachment ID");
12307 return(peMsgCollected(interp, &md, err, flags));
12310 else if(!strucmp(field, "fcc")){
12311 Tcl_Obj **objFcc;
12312 int nFcc;
12314 if(Tcl_ListObjGetElements(interp, objField[1], &nFcc, &objFcc) == TCL_OK
12315 && nFcc == 2
12316 && Tcl_GetIntFromObj(interp, objFcc[0], &md.fcc_colid) == TCL_OK
12317 && (value = Tcl_GetStringFromObj(objFcc[1], NULL))){
12318 if(md.fcc)
12319 fs_give((void **) &md.fcc);
12321 md.fcc = cpystr(value);
12323 else {
12324 strcpy(err = tmp_20k_buf, "Unrecognized Fcc specification");
12325 return(peMsgCollected(interp, &md, err, flags));
12328 else if(!strucmp(field, "postoption")){
12329 Tcl_Obj **objPO;
12330 int nPO, ival;
12332 value = NULL;
12333 if(Tcl_ListObjGetElements(interp, objField[1], &nPO, &objPO) == TCL_OK
12334 && nPO == 2
12335 && (value = Tcl_GetStringFromObj(objPO[0], NULL))){
12336 if(!strucmp(value,"fcc-without-attachments")){
12337 if(Tcl_GetIntFromObj(interp, objPO[1], &ival) == TCL_OK){
12338 md.postop_fcc_no_attach = (ival != 0);
12340 else{
12341 sprintf(err = tmp_20k_buf, "Malformed Post Option: fcc-without-attachments");
12342 return(peMsgCollected(interp, &md, err, flags));
12345 else if(!strucmp(value, "charset")){
12346 if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12347 char *p;
12349 for(p = value; ; p++){ /* sanity check */
12350 if(!*p){
12351 md.charset = cpystr(value);
12352 break;
12355 if(isspace((unsigned char ) *p)
12356 || !isprint((unsigned char) *p))
12357 break;
12359 if(p - value > 255)
12360 break;
12363 else{
12364 err = "Post option read failure";
12365 return(peMsgCollected(interp, &md, err, flags));
12368 else if(!strucmp(value, "flowed")){
12369 if(F_OFF(F_QUELL_FLOWED_TEXT,ps_global)){
12370 if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12371 if(!strucmp(value, "yes"))
12372 md.flowed = 1;
12374 else{
12375 err = "Post option read failure";
12376 return(peMsgCollected(interp, &md, err, flags));
12380 else if(!strucmp(value, "subtype")){
12381 if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12382 if(!strucmp(value, "html"))
12383 md.html = 1;
12385 else{
12386 err = "Post option read failure";
12387 return(peMsgCollected(interp, &md, err, flags));
12390 else if(!strucmp(value, "priority")){
12391 if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
12392 char *priority = NULL;
12394 if(!strucmp(value, "highest"))
12395 priority = "Highest";
12396 else if(!strucmp(value, "high"))
12397 priority = "High";
12398 else if(!strucmp(value, "normal"))
12399 priority = "Normal";
12400 else if(!strucmp(value, "low"))
12401 priority = "Low";
12402 else if(!strucmp(value, "lowest"))
12403 priority = "Lowest";
12405 if(priority){
12406 if((pf = set_priority_header(md.metaenv, priority)) != NULL)
12407 pf->text = &pf->textbuf;
12410 else{
12411 err = "Post option read failure";
12412 return(peMsgCollected(interp, &md, err, flags));
12415 else{
12416 sprintf(err = tmp_20k_buf, "Unknown Post Option: %s", value);
12417 return(peMsgCollected(interp, &md, err, flags));
12420 else{
12421 sprintf(err = tmp_20k_buf, "Malformed Post Option");
12422 return(peMsgCollected(interp, &md, err, flags));
12425 else {
12426 if(nField != 2){
12427 sprintf(err = tmp_20k_buf, "Malformed header (%s)", field);
12428 return(peMsgCollected(interp, &md, err, flags));
12431 if((value = Tcl_GetStringFromObj(objField[1], &vl)) != NULL){
12432 ADDRESS **addrp = NULL;
12433 char **valp = NULL, *valcpy;
12435 if(!strucmp(field, "from")){
12436 addrp = &md.outgoing->from;
12438 else if(!strucmp(field, "reply-to")){
12439 addrp = &md.outgoing->reply_to;
12441 else if(!strucmp(field, "to")){
12442 addrp = &md.outgoing->to;
12444 else if(!strucmp(field, "cc")){
12445 addrp = &md.outgoing->cc;
12447 else if(!strucmp(field, "bcc")){
12448 addrp = &md.outgoing->bcc;
12450 else if(!strucmp(field, "subject")){
12451 valp = &md.outgoing->subject;
12453 else if(!strucmp(field, "in-reply-to")){
12454 valp = &md.outgoing->in_reply_to;
12456 else if(!strucmp(field, "newsgroups")){
12457 valp = &md.outgoing->newsgroups;
12459 else if(!strucmp(field, "followup-to")){
12460 valp = &md.outgoing->followup_to;
12462 else if(!strucmp(field, "references")){
12463 valp = &md.outgoing->references;
12465 else if(!strucmp(field, "x-reply-uid")){
12466 for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
12467 if(!strucmp(pf->name, "x-reply-uid")){
12468 valp = pf->text = &pf->textbuf;
12469 break;
12472 else if(!strucmp(field, "x-auth-received")){
12473 for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
12474 if(!strucmp(pf->name, "x-auth-received")){
12475 valp = pf->text = &pf->textbuf;
12476 break;
12479 else{
12480 for(pf = md.metaenv->custom; pf && pf->name; pf = pf->next)
12481 if(!strucmp(field, pf->name)){
12482 if(pf->type == Address)
12483 addrp = pf->addr;
12484 else if(vl)
12485 valp = &pf->textbuf;
12486 else if(pf->textbuf)
12487 fs_give((void **) &pf->textbuf);
12489 break;
12492 if(!pf)
12493 dprint((2, "\nPOST: unrecognized field - %s\n", field));
12496 if(valp){
12497 if(*valp)
12498 fs_give((void **) valp);
12500 sprintf(*valp = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
12503 if(addrp){
12504 sprintf(valcpy = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
12506 for(; *addrp; addrp = &(*addrp)->next)
12509 rfc822_parse_adrlist(addrp, valcpy,
12510 (flags & PMC_FORCE_QUAL)
12511 ? fakedomain : ps_global->maildomain);
12512 fs_give((void **) &valcpy);
12515 else{
12516 err = "Value read failure";
12517 return(peMsgCollected(interp, &md, err, flags));
12523 return(peMsgCollected(interp, &md, err, flags));
12528 * peMsgCollected - Dispatch collected message data and cleanup
12531 peMsgCollected(Tcl_Interp *interp, MSG_COL_S *md, char *err, long flags)
12533 int rv = TCL_OK, non_ascii = FALSE;
12534 unsigned char c;
12535 BODY *body = NULL, *tbp = NULL;
12536 char errbuf[WP_MAX_POST_ERROR + 1], *charset;
12537 STRLIST_S *lp;
12539 if(err){
12540 if(md->msgtext)
12541 so_give(&md->msgtext);
12543 rv = TCL_ERROR;
12545 else if(md->qualified_addrs && check_addresses(md->metaenv) == CA_BAD){
12546 sprintf(err = tmp_20k_buf, "Address must be fully qualified.");
12547 rv = TCL_ERROR;
12549 else{
12550 /* sniff body for possible multipart wrapping to protect encoding */
12551 so_seek(md->msgtext, 0L, 0);
12553 while(so_readc(&c, md->msgtext))
12554 if(!c || c & 0x80){
12555 non_ascii = TRUE;
12556 break;
12559 if(!md->outgoing->from)
12560 md->outgoing->from = generate_from();
12562 rfc822_date(tmp_20k_buf);
12563 md->outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
12564 md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from);
12565 md->outgoing->message_id = generate_message_id();
12567 body = mail_newbody();
12569 /* wire any attachments to body */
12570 if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global))){
12571 PART **np;
12572 PARAMETER **pp;
12573 COMPATT_S *a;
12575 /* setup slot for message text */
12576 body->type = TYPEMULTIPART;
12577 body->nested.part = mail_newbody_part();
12578 tbp = &body->nested.part->body;
12580 /* link in attachments */
12581 for(lp = md->attach, np = &body->nested.part->next; lp; lp = lp->next, np = &(*np)->next){
12582 if(!(a = peGetAttachID(lp->name))){
12583 err = "Unknown Attachment ID";
12584 rv = TCL_ERROR;
12585 break;
12588 *np = mail_newbody_part();
12590 if(a->file){
12591 (*np)->body.id = generate_message_id();
12592 (*np)->body.description = cpystr(a->l.f.description);
12594 /* set name parameter */
12595 for(pp = &(*np)->body.parameter; *pp; )
12596 if(!struncmp((*pp)->attribute, "name", 4)
12597 && (!*((*pp)->attribute + 4)
12598 || *((*pp)->attribute + 4) == '*')){
12599 PARAMETER *free_me = *pp;
12600 *pp = (*pp)->next;
12601 free_me->next = NULL;
12602 mail_free_body_parameter(&free_me);
12604 else
12605 pp = &(*pp)->next;
12607 *pp = NULL;
12608 set_parameter(pp, "name", a->l.f.remote);
12610 /* Then set the Content-Disposition ala RFC1806 */
12611 if(!(*np)->body.disposition.type){
12612 (*np)->body.disposition.type = cpystr("attachment");
12613 for(pp = &(*np)->body.disposition.parameter; *pp; )
12614 if(!struncmp((*pp)->attribute, "filename", 4)
12615 && (!*((*pp)->attribute + 4)
12616 || *((*pp)->attribute + 4) == '*')){
12617 PARAMETER *free_me = *pp;
12618 *pp = (*pp)->next;
12619 free_me->next = NULL;
12620 mail_free_body_parameter(&free_me);
12622 else
12623 pp = &(*pp)->next;
12625 *pp = NULL;
12626 set_parameter(pp, "filename", a->l.f.remote);
12629 if(((*np)->body.contents.text.data = (void *) so_get(FileStar, a->l.f.local, READ_ACCESS)) != NULL){
12630 (*np)->body.type = mt_translate_type(a->l.f.type);
12631 (*np)->body.subtype = cpystr(a->l.f.subtype);
12632 (*np)->body.encoding = ENCBINARY;
12633 (*np)->body.size.bytes = name_file_size(a->l.f.local);
12635 if((*np)->body.type == TYPEOTHER
12636 && !set_mime_type_by_extension(&(*np)->body, a->l.f.local))
12637 set_mime_type_by_grope(&(*np)->body);
12639 so_release((STORE_S *)(*np)->body.contents.text.data);
12641 else{
12642 /* unravel here */
12643 err = "Can't open uploaded attachment";
12644 rv = TCL_ERROR;
12645 break;
12648 else if(a->body){
12649 BODY *newbody = copy_body(NULL, a->l.b.body);
12650 (*np)->body = *newbody;
12651 fs_give((void **) &newbody);
12652 peBodyMoveContents(a->l.b.body, &(*np)->body);
12654 else{
12655 err = "BOTCH: Unknown attachment type";
12656 rv = TCL_ERROR;
12657 break;
12661 else
12662 tbp = body;
12664 /* assign MIME parameters to text body part */
12665 tbp->type = TYPETEXT;
12666 if(md->html) tbp->subtype = cpystr("HTML");
12668 tbp->contents.text.data = (void *) md->msgtext;
12669 tbp->encoding = ENCOTHER;
12671 /* set any text flowed param */
12672 if(md->flowed)
12673 peMsgSetParm(&tbp->parameter, "format", "flowed");
12675 if(rv == TCL_OK){
12676 CONTEXT_S *fcc_cntxt = ps_global->context_list;
12678 while(md->fcc_colid--)
12679 if(fcc_cntxt->next)
12680 fcc_cntxt = fcc_cntxt->next;
12682 if(md->postop_fcc_no_attach >= 0){
12683 int oldval = F_ON(F_NO_FCC_ATTACH, ps_global);
12684 F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
12685 md->postop_fcc_no_attach = oldval;
12688 pine_encode_body(body);
12690 rv = (*md->postfunc)(md->metaenv, body, md->fcc, &fcc_cntxt, errbuf);
12692 if(md->postop_fcc_no_attach >= 0){
12693 F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
12696 if(rv == TCL_OK){
12697 if((flags & PMC_PRSRV_ATT) == 0)
12698 peFreeAttach(&peCompAttach);
12700 else{
12701 /* maintain pointers to attachments */
12702 (void) peMsgAttachCollector(NULL, body);
12703 err = errbuf;
12707 pine_free_body(&body);
12710 if(md->charset)
12711 fs_give((void **) &md->charset);
12713 free_strlist(&md->attach);
12715 pine_free_env(&md->metaenv);
12717 if(md->custom)
12718 free_customs(md->custom);
12720 mail_free_envelope(&md->outgoing);
12722 if(err && *err)
12723 Tcl_SetResult(interp, err, TCL_VOLATILE);
12725 return(rv);
12729 void
12730 peMsgSetParm(PARAMETER **pp, char *pa, char *pv)
12732 for(; *pp; pp = &(*pp)->next)
12733 if(!strucmp(pa, (*pp)->attribute)){
12734 if((*pp)->value)
12735 fs_give((void **) &(*pp)->value);
12737 break;
12740 if(!*pp){
12741 *pp = mail_newbody_parameter();
12742 (*pp)->attribute = cpystr(pa);
12745 (*pp)->value = cpystr(pv);
12749 Tcl_Obj *
12750 peMsgAttachCollector(Tcl_Interp *interp, BODY *b)
12752 char *id, *name = NULL;
12753 PART *part;
12754 Tcl_Obj *aListObj = NULL, *aObj = NULL;
12756 peFreeAttach(&peCompAttach);
12758 if(interp)
12759 aListObj = Tcl_NewListObj(0, NULL);
12761 if(b->type == TYPEMULTIPART){
12763 * Walk first level, clipping branches and adding them
12764 * to the attachment list...
12766 for(part = b->nested.part->next; part; part = part->next) {
12767 id = peBodyAttachID(&part->body);
12768 aObj = Tcl_NewListObj(0, NULL);
12770 if(interp){
12771 Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(id, -1));
12773 /* name */
12774 if((name = get_filename_parameter(NULL, 0, &part->body, NULL)) != NULL){
12775 Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(name, -1));
12776 fs_give((void **) &name);
12778 else
12779 Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj("Unknown", -1));
12781 /* size */
12782 Tcl_ListObjAppendElement(interp, aObj,
12783 Tcl_NewLongObj((part->body.encoding == ENCBASE64)
12784 ? ((part->body.size.bytes * 3)/4)
12785 : part->body.size.bytes));
12787 /* type */
12788 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
12789 body_type_names(part->body.type),
12790 part->body.subtype ? part->body.subtype : rfc822_default_subtype (part->body.type));
12791 Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(tmp_20k_buf, -1));
12792 Tcl_ListObjAppendElement(interp, aListObj, aObj);
12797 return (aListObj);
12802 peFccAppend(Tcl_Interp *interp, Tcl_Obj *obj, char *fcc, int colid)
12804 Tcl_Obj *objfcc = NULL;
12806 if(colid < 0)
12807 colid = (ps_global->context_list && (ps_global->context_list->use & CNTXT_INCMNG)) ? 1 : 0;
12809 return((objfcc = Tcl_NewListObj(0, NULL))
12810 && Tcl_ListObjAppendElement(interp, objfcc, Tcl_NewStringObj("fcc", -1)) == TCL_OK
12811 && peAppListF(interp, objfcc, "%i%s", colid, fcc) == TCL_OK
12812 && Tcl_ListObjAppendElement(interp, obj, objfcc) == TCL_OK);
12816 /* * * * * * * * * * * * * Start of Address Management Routines * * * * * * * * * * * */
12820 * PEAddressCmd - export various bits of address book/directory access
12823 PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
12825 char *op;
12827 dprint((2, "PEAddressCmd"));
12829 if(objc == 1){
12830 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
12831 return(TCL_ERROR);
12833 else if(!ps_global){
12834 Tcl_SetResult(interp, "PEAddress: no open folder", TCL_STATIC);
12835 return(TCL_ERROR);
12837 else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
12838 if(objc == 2){
12839 if(!strcmp(op, "safecheck")){
12840 if(peInitAddrbooks(interp, 1) != TCL_OK)
12841 return(TCL_ERROR);
12842 return(TCL_OK);
12844 else if(!strcmp(op, "books")){
12845 int i;
12848 * return the list of configured address books
12851 if(peInitAddrbooks(interp, 0) != TCL_OK)
12852 return(TCL_ERROR);
12854 for(i = 0; i < as.n_addrbk; i++){
12855 Tcl_Obj *objmv[4];
12857 objmv[0] = Tcl_NewIntObj(i);
12858 if(as.adrbks[i].abnick){
12859 objmv[1] = Tcl_NewStringObj(as.adrbks[i].abnick, -1);
12861 else {
12862 char buf[256];
12864 snprintf(buf, sizeof(buf), "Address book number %d", i + 1);
12865 objmv[1] = Tcl_NewStringObj(buf, -1);
12868 objmv[2] = Tcl_NewStringObj(as.adrbks[i].filename ? as.adrbks[i].filename : "", -1);
12870 objmv[3] = Tcl_NewIntObj(as.adrbks[i].access == ReadWrite);
12872 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
12873 Tcl_NewListObj(4, objmv));
12876 return(TCL_OK);
12879 else if(objc == 3){
12880 if(!strcmp(op, "parselist")){
12881 char *addrstr;
12882 ADDRESS *addrlist = NULL, *atmp, *anextp;
12883 static char *fakedomain = "@";
12885 if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
12886 addrstr = cpystr(addrstr); /* can't munge tcl copy */
12888 ps_global->c_client_error[0] = '\0';
12889 rfc822_parse_adrlist(&addrlist, addrstr,
12890 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
12891 ? fakedomain : ps_global->maildomain);
12893 fs_give((void **) &addrstr);
12894 if(ps_global->c_client_error[0]){
12895 Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
12896 return(TCL_ERROR);
12899 for(atmp = addrlist; atmp; ){
12900 RFC822BUFFER rbuf;
12902 anextp = atmp->next;
12903 atmp->next = NULL;
12904 tmp_20k_buf[0] = '\0';
12905 rbuf.f = dummy_soutr;
12906 rbuf.s = NULL;
12907 rbuf.beg = tmp_20k_buf;
12908 rbuf.cur = tmp_20k_buf;
12909 rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
12910 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
12911 *rbuf.cur = '\0';
12912 Tcl_ListObjAppendElement(interp,
12913 Tcl_GetObjResult(interp),
12914 Tcl_NewStringObj(tmp_20k_buf, -1));
12915 atmp = anextp;
12918 mail_free_address(&addrlist);
12919 return(TCL_OK);
12921 else if(!strcmp(op, "xlookup")){
12922 char *addrstr;
12923 ADDRESS *addrlist = NULL, *atmp, *anextp;
12924 static char *fakedomain = "@";
12926 if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
12927 addrstr = cpystr(addrstr); /* can't munge tcl copy */
12929 ps_global->c_client_error[0] = '\0';
12930 rfc822_parse_adrlist(&addrlist, addrstr,
12931 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
12932 ? fakedomain : ps_global->maildomain);
12934 fs_give((void **) &addrstr);
12935 if(ps_global->c_client_error[0]){
12936 Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
12937 return(TCL_ERROR);
12940 for(atmp = addrlist; atmp; ){
12941 anextp = atmp->next;
12942 atmp->next = NULL;
12943 tmp_20k_buf[0] = '\0';
12944 if(atmp->host){
12945 if(atmp->host[0] == '@'){
12946 /* leading ampersand means "missing-hostname" */
12948 else{
12949 RFC822BUFFER rbuf;
12951 rbuf.f = dummy_soutr;
12952 rbuf.s = NULL;
12953 rbuf.beg = tmp_20k_buf;
12954 rbuf.cur = tmp_20k_buf;
12955 rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
12956 rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
12957 *rbuf.cur = '\0';
12958 Tcl_ListObjAppendElement(interp,
12959 Tcl_GetObjResult(interp),
12960 Tcl_NewStringObj(tmp_20k_buf, -1));
12962 } /* else group syntax, move on */
12964 atmp = anextp;
12967 mail_free_address(&addrlist);
12968 return(TCL_OK);
12970 else if(!strcmp(op, "format")){
12971 int i, booknum;
12972 char buf[256], *s;
12974 if(peInitAddrbooks(interp, 0) != TCL_OK)
12975 return(TCL_ERROR);
12980 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
12981 for(i = 0; i < as.n_addrbk; i++)
12982 if(i == booknum){
12983 addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
12985 for(i = 0; i < NFIELDS && as.adrbks[booknum].disp_form[i].type != Notused; i++){
12986 switch(as.adrbks[booknum].disp_form[i].type){
12987 case Nickname :
12988 s = "nick";
12989 break;
12990 case Fullname :
12991 s = "full";
12992 break;
12993 case Addr :
12994 s = "addr";
12995 break;
12996 case Filecopy :
12997 s = "fcc";
12998 break;
12999 case Comment :
13000 s = "comment";
13001 break;
13002 default :
13003 s = NULL;
13004 break;
13007 if(s){
13008 Tcl_Obj *objmv[2];
13010 objmv[0] = Tcl_NewStringObj(s, -1);
13011 objmv[1] = Tcl_NewIntObj((100 * as.adrbks[booknum].disp_form[i].width) / ps_global->ttyo->screen_cols);
13012 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13013 Tcl_NewListObj(2, objmv));
13018 return(TCL_OK);
13021 snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
13022 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13023 return(TCL_ERROR);
13025 else if(!strcmp(op, "list")){
13026 int i, j, k, n, booknum;
13027 char buf[256], *s;
13028 AdrBk_Entry *ae;
13029 Tcl_Obj *objev[NFIELDS + 1], *objhv[2];
13031 if(peInitAddrbooks(interp, 0) != TCL_OK)
13032 return(TCL_ERROR);
13037 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
13038 for(i = 0; i < as.n_addrbk; i++)
13039 if(i == booknum){
13040 addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
13042 for(i = 0;
13043 (ae = adrbk_get_ae(as.adrbks[booknum].address_book, i));
13044 i++){
13046 /* first member is type: Single, List or Lookup */
13047 switch(ae->tag){
13048 case Single :
13049 s = "single";
13050 break;
13051 case List :
13052 s = "list";
13053 break;
13054 default : /* not set!?! */
13055 continue;
13058 if(!ae->nickname)
13059 continue;
13061 objhv[0] = Tcl_NewStringObj(ae->nickname, -1);
13062 objhv[1] = Tcl_NewStringObj(s, -1);
13063 objev[n = 0] = Tcl_NewListObj(2, objhv);
13066 * set fields based on VAR_ABOOK_FORMATS
13069 for(j = 0; j < NFIELDS && as.adrbks[booknum].disp_form[j].type != Notused; j++){
13070 switch(as.adrbks[booknum].disp_form[j].type){
13071 case Nickname :
13072 objev[++n] = Tcl_NewStringObj(ae->nickname, -1);
13073 break;
13074 case Fullname :
13075 objev[++n] = Tcl_NewStringObj(ae->fullname, -1);
13076 break;
13077 case Addr :
13078 if(ae->tag == Single){
13079 objev[++n] = Tcl_NewStringObj(ae->addr.addr, -1);
13081 else{
13082 Tcl_Obj **objav;
13084 for(k = 0; ae->addr.list[k]; k++)
13087 objav = (Tcl_Obj **) fs_get(k * sizeof(Tcl_Obj *));
13088 for(k = 0; ae->addr.list[k]; k++)
13089 objav[k] = Tcl_NewStringObj(ae->addr.list[k], -1);
13091 objev[++n] = Tcl_NewListObj(k, objav);
13092 fs_give((void **) &objav);
13094 break;
13095 case Filecopy :
13096 objev[++n] = Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1);
13097 break;
13098 case Comment :
13099 objev[++n] = Tcl_NewStringObj(ae->extra ? ae->extra : "", -1);
13100 break;
13101 default :
13102 s = NULL;
13103 break;
13107 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13108 Tcl_NewListObj(n + 1, objev));
13111 return(TCL_OK);
13114 snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
13115 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13116 return(TCL_ERROR);
13119 else if(objc == 4){
13120 if(!strcmp(op, "verify")){
13122 * The object here is to check the following list of field values
13123 * to see that they are valid address list, expanding if necessary.
13124 * The first argument is the list of field values, with "to" being
13125 * first. The second arg is the current fcc value.
13127 * The return value is of the following form:
13129 * { {{errstr {{oldstr newstr {ldap-opts ...}} ...}} ...} newfcc}
13131 Tcl_Obj **objVal;
13132 char *addrstr, *newaddr = NULL, *error = NULL,
13133 *tstr1, *tstr2, *fcc, *newfcc = NULL;
13134 BuildTo toaddr;
13135 int rv, badadrs, i , numlistvals,
13136 numldapqueries = 0;
13137 Tcl_Obj *resObj = NULL, *secObj, *strObj, *adrObj, *res2Obj;
13138 #ifdef ENABLE_LDAP
13139 WPLDAPRES_S **tsl;
13141 wpldap_global->query_no++;
13142 if(wpldap_global->ldap_search_list){
13143 wpldap_global->ldap_search_list =
13144 free_wpldapres(wpldap_global->ldap_search_list);
13147 tsl = &(wpldap_global->ldap_search_list);
13148 #endif /* ENABLE_LDAP */
13150 if(Tcl_ListObjGetElements(interp, objv[2], &numlistvals,
13151 &objVal) == TCL_OK){
13152 if((fcc = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
13153 return TCL_ERROR;
13154 res2Obj = Tcl_NewListObj(0, NULL);
13155 for(i = 0; i < numlistvals; i++){
13156 size_t l;
13158 if((addrstr = Tcl_GetStringFromObj(objVal[i], NULL)) == NULL)
13159 return TCL_ERROR;
13161 addrstr = cpystr(addrstr); /* can't munge tcl copy */
13162 toaddr.type = Str;
13163 toaddr.arg.str = cpystr(addrstr);
13164 l = strlen(addrstr);
13165 badadrs = 0;
13166 resObj = Tcl_NewListObj(0, NULL);
13167 secObj = Tcl_NewListObj(0, NULL);
13168 for(tstr1 = addrstr; tstr1; tstr1 = tstr2){
13169 tstr2 = strqchr(tstr1, ',', 0, -1);
13170 if(tstr2)
13171 *tstr2 = '\0';
13173 strncpy(toaddr.arg.str, tstr1, l);
13174 toaddr.arg.str[l] = '\0';
13176 removing_leading_and_trailing_white_space(toaddr.arg.str);
13177 if(*toaddr.arg.str){
13178 if(i == 0 && tstr1 == addrstr)
13179 newfcc = cpystr(fcc);
13181 rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
13183 if(rv == 0){
13184 strObj = Tcl_NewListObj(0, NULL);
13185 Tcl_ListObjAppendElement(interp, strObj,
13186 Tcl_NewStringObj(toaddr.arg.str, -1));
13187 Tcl_ListObjAppendElement(interp, strObj,
13188 Tcl_NewStringObj(newaddr,-1));
13189 /* append whether or not ldap stuff
13190 * was returned
13192 adrObj = Tcl_NewListObj(0,NULL);
13193 #ifdef ENABLE_LDAP
13194 if(*tsl) {
13195 LDAP_CHOOSE_S *tres;
13196 LDAP_SERV_RES_S *trl;
13197 LDAPMessage *e;
13198 ADDRESS *newadr;
13199 char *ret_to;
13201 tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
13202 for(trl = (*tsl)->reslist; trl;
13203 trl = trl->next){
13204 for(e = ldap_first_entry(trl->ld,
13205 trl->res);
13206 e != NULL;
13207 e = ldap_next_entry(trl->ld, e)){
13208 tres->ld = trl->ld;
13209 tres->selected_entry = e;
13210 tres->info_used = trl->info_used;
13211 tres->serv = trl->serv;
13212 if((newadr = address_from_ldap(tres)) != NULL){
13213 if(newadr->mailbox && newadr->host){
13214 RFC822BUFFER rbuf;
13215 size_t len;
13217 len = est_size(newadr);
13218 ret_to = (char *)fs_get(len * sizeof(char));
13219 ret_to[0] = '\0';
13220 rbuf.f = dummy_soutr;
13221 rbuf.s = NULL;
13222 rbuf.beg = ret_to;
13223 rbuf.cur = ret_to;
13224 rbuf.end = ret_to+len-1;
13225 rfc822_output_address_list(&rbuf, newadr, 0L, NULL);
13226 *rbuf.cur = '\0';
13227 Tcl_ListObjAppendElement(interp,
13228 adrObj, Tcl_NewStringObj(ret_to, -1));
13229 fs_give((void **)&ret_to);
13231 mail_free_address(&newadr);
13235 fs_give((void **)&tres);
13236 numldapqueries++;
13237 tsl = &((*tsl)->next);
13239 #endif /* ENABLE_LDAP */
13240 Tcl_ListObjAppendElement(interp, strObj, adrObj);
13241 Tcl_ListObjAppendElement(interp, secObj, strObj);
13243 else {
13244 badadrs = 1;
13245 break;
13248 if(tstr2){
13249 *tstr2 = ',';
13250 tstr2++;
13253 resObj = Tcl_NewListObj(0, NULL);
13254 Tcl_ListObjAppendElement(interp, resObj,
13255 Tcl_NewStringObj(badadrs
13256 ? (error ? error : "Unknown")
13257 : "", -1));
13258 Tcl_ListObjAppendElement(interp, resObj, secObj);
13259 Tcl_ListObjAppendElement(interp, res2Obj, resObj);
13260 fs_give((void **) &addrstr);
13261 fs_give((void **) &toaddr.arg.str);
13263 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), res2Obj);
13264 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13265 Tcl_NewStringObj(newfcc ? newfcc
13266 : (fcc ? fcc : ""), -1));
13267 if(newfcc) fs_give((void **)&newfcc);
13268 return(TCL_OK);
13270 return(TCL_ERROR);
13272 else if(!strcmp(op, "expand")){
13273 BuildTo toaddr;
13274 char *addrstr, *newaddr = NULL, *error = NULL, *fcc, *newfcc = NULL;
13275 int rv;
13278 * Return value will be of the form:
13279 * {"addrstr",
13280 * ldap-query-number,
13281 * "fcc"
13284 * ldap-query-number will be nonzero if
13285 * there is something interesting to display as a result
13286 * of an ldap query.
13290 * Given what looks like an rfc822 address line, parse the
13291 * contents and expand any tokens that look like they ought
13292 * to be.
13295 if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
13296 toaddr.type = Str;
13297 toaddr.arg.str = cpystr(addrstr); /* can't munge tcl copy */
13298 fcc = Tcl_GetStringFromObj(objv[3], NULL);
13299 #ifdef ENABLE_LDAP
13300 wpldap_global->query_no++;
13301 if(wpldap_global->ldap_search_list){
13302 wpldap_global->ldap_search_list =
13303 free_wpldapres(wpldap_global->ldap_search_list);
13305 #endif /* ENABLE_LDAP */
13306 newfcc = cpystr(fcc);
13307 rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
13308 fs_give((void **) &toaddr.arg.str);
13309 if(rv == 0){
13310 #ifdef ENABLE_LDAP
13312 * c-client quotes results with spaces in them, so we'll go
13313 * through and unquote them.
13315 if(wpldap_global->ldap_search_list){
13316 WPLDAPRES_S *tres;
13317 char *tstr1, *tstr2;
13318 char *qstr1, *newnewaddr;
13319 int qstr1len;
13321 for(tres = wpldap_global->ldap_search_list;
13322 tres; tres = tres->next){
13323 if(strqchr(tres->str, ' ', 0, -1)){
13324 qstr1len = strlen(tres->str) + 3;
13325 qstr1 = (char *)fs_get(qstr1len*sizeof(char));
13326 snprintf(qstr1, qstr1len, "\"%.*s\"", qstr1len, tres->str);
13327 for(tstr1 = newaddr; tstr1; tstr1 = tstr2){
13328 tstr2 = strqchr(tstr1, ',', 0, -1);
13329 if(strncmp(qstr1, tstr1, tstr2 ? tstr2 - tstr1
13330 : strlen(tstr1)) == 0){
13331 size_t l;
13332 l = strlen(newaddr) + strlen(tres->str) + 2
13333 + (tstr2 ? strlen(tstr2) : 0);
13334 newnewaddr = (char *) fs_get(l * sizeof(char));
13335 snprintf(newnewaddr, l, "%.*s%s%s", (int) (tstr1 - newaddr),
13336 newaddr, tres->str, tstr2 ? tstr2 : "");
13337 fs_give((void **)&newaddr);
13338 newaddr = newnewaddr;
13339 break;
13341 if(tstr2)
13342 tstr2++;
13343 if(tstr2 && *tstr2 == ' ')
13344 tstr2++;
13346 if(qstr1)
13347 fs_give((void **) &qstr1);
13351 #endif /* ENABLE_LDAP */
13352 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13353 Tcl_NewStringObj(newaddr, -1)) != TCL_OK)
13354 return(TCL_ERROR);
13355 #ifdef ENABLE_LDAP
13356 if(wpldap_global->ldap_search_list){
13357 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13358 Tcl_NewIntObj(wpldap_global->query_no)) != TCL_OK)
13359 return(TCL_ERROR);
13361 else
13362 #endif /* ENABLE_LDAP */
13363 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13364 Tcl_NewIntObj(0)) != TCL_OK)
13365 return(TCL_ERROR);
13366 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13367 Tcl_NewStringObj(newfcc ? newfcc
13368 : (fcc ? fcc : ""), -1)) != TCL_OK)
13369 return(TCL_ERROR);
13370 if(newfcc) fs_give((void **)&newfcc);
13372 return(TCL_OK);
13374 else{
13375 Tcl_SetResult(interp, error ? error : "Indeterminate error", TCL_VOLATILE);
13376 if(newfcc) fs_give((void **)&newfcc);
13377 return(TCL_ERROR);
13381 else if(!strcmp(op, "complete")){
13383 * CMD: complete uid
13385 * Look for possible completions for
13386 * given query_string.
13388 * ARGS: <query_string> <uid>
13390 * Returns: candidate list: {nickname {personal mailbox}}
13392 char *query, *errstr;
13393 long uid;
13394 COMPLETE_S *completions, *cp;
13396 if(peInitAddrbooks(interp, 0) == TCL_OK){
13397 if((query = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
13398 if(Tcl_GetLongFromObj(interp, objv[3], &uid) == TCL_OK){
13400 completions = adrbk_list_of_completions(query,
13401 ps_global->mail_stream,
13402 uid,
13403 #ifdef ENABLE_LDAP
13404 ((strlen(query) >= 5) ? ALC_INCLUDE_LDAP : 0) |
13405 #endif /* ENABLE_LDAP */
13408 if(completions){
13409 for(cp = completions; cp; cp = cp->next)
13410 peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s",
13411 cp->nickname ? cp->nickname : "",
13412 cp->full_address ? cp->full_address : "",
13413 cp->fcc ? cp->fcc : "");
13415 free_complete_s(&completions);
13417 else
13418 Tcl_SetResult(interp, "", TCL_STATIC);
13420 return(TCL_OK);
13422 else
13423 errstr = "PEAddress: Cannot read UID";
13425 else
13426 errstr = "PEAddress: Cannot get completion query";
13428 else
13429 errstr = "PEAddress: Address Book initialization failed";
13431 Tcl_SetResult(interp, errstr, TCL_STATIC);
13432 return(TCL_ERROR);
13435 else if(objc == 5){
13436 if(!strcmp(op, "entry")){
13437 int booknum, i, aindex;
13438 char *nick, *astr = NULL, *errstr = NULL, *fccstr = NULL, buf[128];
13439 AdrBk_Entry *ae;
13440 BuildTo bldto;
13442 if(peInitAddrbooks(interp, 0) != TCL_OK)
13443 return(TCL_ERROR);
13446 * Given an address book handle and nickname, return address
13448 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
13449 for(i = 0; i < as.n_addrbk; i++)
13450 if(i == booknum){
13451 if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL){
13452 Tcl_SetResult(interp, "PEAddress list: Can't get nickname", TCL_STATIC);
13453 return(TCL_ERROR);
13455 if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
13456 || (*nick == '\0' && aindex < 0)){
13457 Tcl_SetResult(interp, "PEAddress list: Can't get aindex", TCL_STATIC);
13458 return(TCL_ERROR);
13460 if((*nick)
13461 ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
13462 : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
13463 bldto.type = Abe;
13464 bldto.arg.abe = ae;
13466 (void) our_build_address(bldto, &astr, &errstr, &fccstr, NULL);
13468 if(errstr){
13469 if(astr)
13470 fs_give((void **) &astr);
13472 Tcl_SetResult(interp, errstr, TCL_VOLATILE);
13473 return(TCL_ERROR);
13476 if(astr){
13477 char *p;
13478 int l;
13480 l = (4*strlen(astr) + 1) * sizeof(char);
13481 p = (char *) fs_get(l);
13482 if(rfc1522_decode_to_utf8((unsigned char *) p, l, astr) == (unsigned char *) p){
13483 fs_give((void **) &astr);
13484 astr = p;
13486 else
13487 fs_give((void **)&p);
13491 if(astr){
13492 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13493 Tcl_NewStringObj(astr, -1));
13494 fs_give((void **) &astr);
13496 if(fccstr){
13497 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13498 Tcl_NewStringObj(*fccstr ? fccstr : "\"\"", -1));
13499 fs_give((void **) &fccstr);
13501 else
13502 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
13503 Tcl_NewStringObj("", -1));
13505 else
13506 Tcl_SetResult(interp, "", TCL_STATIC);
13508 return(TCL_OK);
13511 snprintf(buf, sizeof(buf), "PEAddress list: unknown address book ID %d", booknum);
13512 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13513 return(TCL_ERROR);
13515 else if(!strcmp(op, "fullentry")){
13516 int booknum, j, aindex;
13517 char *nick;
13518 AdrBk_Entry *ae;
13519 Tcl_Obj *resObj;
13521 if(peInitAddrbooks(interp, 0) != TCL_OK)
13522 return(TCL_ERROR);
13525 * Given an address book handle and nickname, return
13526 * nickname, fullname, address(es), fcc, and comments
13528 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13529 if(booknum >= 0 && booknum < as.n_addrbk){
13530 if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
13531 return(TCL_ERROR);
13532 if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
13533 || (*nick == '\0' && aindex < 0))
13534 return(TCL_ERROR);
13535 if((*nick)
13536 ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
13537 : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
13538 Tcl_ListObjAppendElement(interp,
13539 Tcl_GetObjResult(interp),
13540 Tcl_NewStringObj(ae->nickname ? ae->nickname : "", -1));
13541 Tcl_ListObjAppendElement(interp,
13542 Tcl_GetObjResult(interp),
13543 Tcl_NewStringObj(ae->fullname ? ae->fullname : "", -1));
13544 resObj = Tcl_NewListObj(0,NULL);
13545 if(ae->tag == Single)
13546 Tcl_ListObjAppendElement(interp,
13547 resObj,
13548 Tcl_NewStringObj(ae->addr.addr ? ae->addr.addr : "", -1));
13549 else {
13550 for(j = 0; ae->addr.list[j]; j++)
13551 Tcl_ListObjAppendElement(interp, resObj,
13552 Tcl_NewStringObj(ae->addr.list[j] ? ae->addr.list[j] : "", -1));
13554 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj);
13555 Tcl_ListObjAppendElement(interp,
13556 Tcl_GetObjResult(interp),
13557 Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1));
13558 Tcl_ListObjAppendElement(interp,
13559 Tcl_GetObjResult(interp),
13560 Tcl_NewStringObj(ae->extra ? ae->extra : "", -1));
13561 return(TCL_OK);
13565 return(TCL_ERROR);
13567 else if(!strcmp(op, "delete")){
13568 char *nick, buf[256];
13569 int booknum, aindex;
13570 adrbk_cntr_t old_entry;
13571 AdrBk *ab;
13572 if(peInitAddrbooks(interp, 0) != TCL_OK){
13573 snprintf(buf, sizeof(buf), "PEAddress delete: couldn't init addressbooks");
13574 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13575 return(TCL_ERROR);
13577 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13578 nick = Tcl_GetStringFromObj(objv[3], NULL);
13579 removing_leading_and_trailing_white_space(nick);
13581 else
13582 return(TCL_ERROR);
13583 if(booknum >= 0 && booknum < as.n_addrbk) {
13584 if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
13585 ab = as.adrbks[booknum].address_book;
13587 else{
13588 snprintf(buf, sizeof(buf), "PEAddress delete: Book number out of range");
13589 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13590 return(TCL_ERROR);
13592 if((Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK)
13593 || (*nick == '\0' && aindex < 0))
13594 return(TCL_ERROR);
13595 adrbk_check_validity(ab, 1L);
13596 if(ab->flags & FILE_OUTOFDATE ||
13597 (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
13598 Tcl_SetResult(interp,
13599 "Address book out of sync. Cannot update at this moment",
13600 TCL_VOLATILE);
13601 return(TCL_ERROR);
13603 if(!nick){
13604 snprintf(buf, sizeof(buf), "PEAddress delete: No nickname");
13605 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13606 return(TCL_ERROR);
13608 if((*nick)
13609 ? (!adrbk_lookup_by_nick(ab, nick, &old_entry))
13610 : ((old_entry = (adrbk_cntr_t)aindex) == -1)){
13611 snprintf(buf, sizeof(buf), "PEAddress delete: Nickname \"%.128s\" not found", nick);
13612 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13613 return(TCL_ERROR);
13615 if(adrbk_delete(ab, old_entry, 0, 0, 1, 1)){
13616 snprintf(buf, sizeof(buf), "PEAddress delete: Couldn't delete addressbook entry");
13617 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13618 return(TCL_ERROR);
13620 return(TCL_OK);
13623 else if((objc == 10 || objc == 11) && !strcmp(op, "edit")){
13624 if(!strcmp(op, "edit")){
13625 int booknum, adri, add, rv, aindex;
13626 char *nick, *fn, *fcc, *comment, *addrfield,
13627 buf[256], **addrs, *orignick = NULL;
13628 AdrBk_Entry *ae = NULL;
13629 AdrBk *ab;
13630 adrbk_cntr_t old_entry = NO_NEXT, new_entry;
13631 Tag tag;
13632 ADDRESS *adr = NULL;
13635 if(peInitAddrbooks(interp, 0) != TCL_OK)
13636 return(TCL_ERROR);
13637 if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
13638 if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
13639 nick = Tcl_GetStringFromObj(objv[3], NULL);
13640 removing_leading_and_trailing_white_space(nick);
13641 if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK){
13642 Tcl_SetResult(interp, "No Address Handle", TCL_VOLATILE);
13643 return(TCL_ERROR);
13645 fn = Tcl_GetStringFromObj(objv[5], NULL);
13646 removing_leading_and_trailing_white_space(fn);
13647 if(!*fn) fn = NULL;
13648 addrfield = Tcl_GetStringFromObj(objv[6], NULL);
13649 removing_leading_and_trailing_white_space(addrfield);
13650 if(!*addrfield) addrfield = NULL;
13652 if(Tcl_ListObjGetElements(interp, objv[7], &numlistvals, &objVal) != TCL_OK)
13653 return(TCL_ERROR);
13655 fcc = Tcl_GetStringFromObj(objv[7], NULL);
13656 removing_leading_and_trailing_white_space(fcc);
13657 if(!*fcc) fcc = NULL;
13658 comment = Tcl_GetStringFromObj(objv[8], NULL);
13659 removing_leading_and_trailing_white_space(comment);
13660 if(!*comment) comment = NULL;
13661 if(Tcl_GetIntFromObj(interp, objv[9], &add) != TCL_OK)
13662 return(TCL_ERROR);
13663 if(objc == 11) {
13665 * if objc == 11 then that means that they changed the
13666 * value of nick to something else, and this one is the
13667 * original nick
13669 orignick = Tcl_GetStringFromObj(objv[10], NULL);
13670 removing_leading_and_trailing_white_space(orignick);
13672 if((addrs = parse_addrlist(addrfield)) != NULL){
13673 int tbuflen = strlen(addrfield);
13674 char *tbuf;
13675 if(!(tbuf = (char *) fs_get(sizeof(char) * (tbuflen+128)))){
13676 Tcl_SetResult(interp, "malloc error", TCL_VOLATILE);
13677 fs_give((void **) &addrs);
13678 return(TCL_ERROR);
13680 for(adri = 0; addrs[adri]; adri++){
13681 if(*(addrs[adri])){
13682 ps_global->c_client_error[0] = '\0';
13683 strncpy(tbuf, addrs[adri], tbuflen+128);
13684 tbuf[tbuflen+128-1] = '\0';
13685 rfc822_parse_adrlist(&adr, tbuf, "@");
13686 if(adr) mail_free_address(&adr);
13687 adr = NULL;
13688 if(ps_global->c_client_error[0]){
13689 snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s",
13690 addrs[adri], strlen(addrs[adri]) > 10 ?
13691 "..." : "", ps_global->c_client_error);
13692 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13693 if(tbuf)
13694 fs_give((void **) &tbuf);
13695 fs_give((void **) &addrs);
13696 return(TCL_ERROR);
13700 if(tbuf) fs_give((void **)&tbuf);
13702 else adri = 0;
13704 /* addrs[adri] = NULL; */
13706 if(adri > 1) tag = List;
13707 else tag = Single;
13709 if(booknum >= 0 && booknum < as.n_addrbk) {
13710 ab = as.adrbks[booknum].address_book;
13712 else{
13713 if(addrs)
13714 fs_give((void **) &addrs);
13715 return(TCL_ERROR);
13717 adrbk_check_validity(ab, 1L);
13718 if(ab->flags & FILE_OUTOFDATE ||
13719 (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
13720 Tcl_SetResult(interp,
13721 "Address book out of sync. Cannot update at this moment",
13722 TCL_VOLATILE);
13723 return(TCL_ERROR);
13725 if(aindex >= 0){
13726 ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex);
13727 if(ae){
13728 old_entry = (adrbk_cntr_t) aindex;
13730 else{
13731 Tcl_SetResult(interp, "No Address Handle!", TCL_VOLATILE);
13732 return(TCL_ERROR);
13735 else if(nick && *nick && adrbk_lookup_by_nick(ab, nick, NULL)){
13736 snprintf(buf, sizeof(buf), "Entry with nickname %.128s already exists.",
13737 nick);
13738 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13739 if(addrs)
13740 fs_give((void **) &addrs);
13741 return(TCL_ERROR);
13743 if(ae &&
13744 ((tag == List && ae->tag == Single) ||
13745 (tag == Single && ae->tag == List))){
13746 if(adrbk_delete(ab, old_entry, 0,0,1,0)){
13747 snprintf(buf, sizeof(buf), "Problem updating from %s to %s.",
13748 ae->tag == Single ? "Single" : "List",
13749 tag == List ? "List" : "Single");
13750 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13751 if(addrs)
13752 fs_give((void **) &addrs);
13753 return(TCL_ERROR);
13755 old_entry = NO_NEXT;
13757 if((rv = adrbk_add(ab, old_entry,
13758 nick ? nick : "",
13759 fn ? fn : "",
13760 tag == List ? (char *)addrs :
13761 (addrs && *addrs) ? *addrs : "",
13762 fcc ? fcc : "",
13763 comment ? comment : "",
13764 tag, &new_entry, NULL, 0, 1,
13765 tag == List ? 0 : 1)) != 0){
13766 snprintf(buf, sizeof(buf), "Couldn't add entry! rv=%d.", rv);
13767 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13768 if(addrs)
13769 fs_give((void **) &addrs);
13770 return(TCL_ERROR);
13772 if(tag == List) {
13773 adrbk_listdel_all(ab, new_entry);
13774 adrbk_nlistadd(ab, new_entry, NULL, NULL, addrs, 0, 1, 1);
13776 return(TCL_OK);
13778 snprintf(buf, sizeof(buf), "Unknown address book ID %d", booknum);
13779 Tcl_SetResult(interp, buf, TCL_VOLATILE);
13780 return(TCL_ERROR);
13785 Tcl_SetResult(interp, "PEAddress: unrecognized command", TCL_STATIC);
13786 return(TCL_ERROR);
13791 peInitAddrbooks(Tcl_Interp *interp, int safe)
13793 if(ps_global->remote_abook_validity > 0)
13794 (void)adrbk_check_and_fix_all(safe, 0, 0);
13796 if(!init_addrbooks(NoDisplay, 1, 1, 0)){
13797 Tcl_SetResult(interp, "No Address Book Configured", TCL_STATIC);
13798 return(TCL_ERROR);
13801 return(TCL_OK);
13807 peRuleStatVal(char *str, int *n)
13809 if(!str)
13810 return(1);
13812 if(!strcmp(str, "either"))
13813 *n = PAT_STAT_EITHER;
13814 else if(!strcmp(str, "yes"))
13815 *n = PAT_STAT_YES;
13816 else if(!strcmp(str, "no"))
13817 *n = PAT_STAT_NO;
13818 else
13819 return 1;
13821 return 0;
13825 #define RS_RULE_EDIT 0x0001
13826 #define RS_RULE_ADD 0x0002
13827 #define RS_RULE_DELETE 0x0004
13828 #define RS_RULE_SHUFFUP 0x0008
13829 #define RS_RULE_SHUFFDOWN 0x0010
13830 #define RS_RULE_GETPAT 0x0100
13831 #define RS_RULE_FINDPAT 0x0200
13834 peRuleSet(Tcl_Interp *interp, Tcl_Obj **objv)
13836 char *rule, *patvar, *patval, *actvar, *actval, *tstr, *ruleaction;
13837 int rno, nPat, nPatEmnt, nAct, nActEmnt, i, rv = 0;
13838 Tcl_Obj **objPat, **objPatEmnt, **objAct, **objActEmnt;
13839 long rflags = PAT_USE_CHANGED, aflags = 0;
13840 PAT_STATE pstate;
13841 PAT_S *pat, *new_pat;
13843 if(!(rule = Tcl_GetStringFromObj(objv[0], NULL)))
13844 return(TCL_ERROR);
13846 if(!(ruleaction = Tcl_GetStringFromObj(objv[1], NULL)))
13847 return(TCL_ERROR);
13849 if(Tcl_GetIntFromObj(interp, objv[2], &rno) == TCL_ERROR)
13850 return(TCL_ERROR);
13852 if(!(strcmp(rule, "filter")))
13853 rflags |= ROLE_DO_FILTER;
13854 else if(!(strcmp(rule, "score")))
13855 rflags |= ROLE_DO_SCORES;
13856 else if(!(strcmp(rule, "indexcolor")))
13857 rflags |= ROLE_DO_INCOLS;
13858 else
13859 return(TCL_ERROR);
13861 if(!(strcmp(ruleaction, "edit"))){
13862 aflags |= RS_RULE_EDIT;
13863 aflags |= RS_RULE_GETPAT;
13864 aflags |= RS_RULE_FINDPAT;
13866 else if(!(strcmp(ruleaction, "add"))){
13867 aflags |= RS_RULE_ADD;
13868 aflags |= RS_RULE_GETPAT;
13870 else if(!(strcmp(ruleaction, "delete"))){
13871 aflags |= RS_RULE_DELETE;
13872 aflags |= RS_RULE_FINDPAT;
13874 else if(!(strcmp(ruleaction, "shuffup"))){
13875 aflags |= RS_RULE_SHUFFUP;
13876 aflags |= RS_RULE_FINDPAT;
13878 else if(!(strcmp(ruleaction, "shuffdown"))){
13879 aflags |= RS_RULE_SHUFFDOWN;
13880 aflags |= RS_RULE_FINDPAT;
13882 else return(TCL_ERROR);
13884 if(aflags & RS_RULE_FINDPAT){
13885 if(any_patterns(rflags, &pstate)){
13886 for(pat = first_pattern(&pstate), i = 0;
13887 pat && i != rno;
13888 pat = next_pattern(&pstate), i++);
13889 if(i != rno) return(TCL_ERROR);
13892 if(aflags & RS_RULE_GETPAT){
13893 int tcl_error = 0;
13895 Tcl_ListObjGetElements(interp, objv[3], &nPat, &objPat);
13896 Tcl_ListObjGetElements(interp, objv[4], &nAct, &objAct);
13898 new_pat = (PAT_S *)fs_get(sizeof(PAT_S));
13899 memset(new_pat, 0, sizeof(PAT_S));
13900 new_pat->patgrp = (PATGRP_S *)fs_get(sizeof(PATGRP_S));
13901 memset(new_pat->patgrp, 0, sizeof(PATGRP_S));
13902 new_pat->action = (ACTION_S *)fs_get(sizeof(ACTION_S));
13903 memset(new_pat->action, 0, sizeof(ACTION_S));
13905 /* Set up the pattern group */
13906 for(i = 0; i < nPat; i++){
13907 Tcl_ListObjGetElements(interp, objPat[i], &nPatEmnt, &objPatEmnt);
13908 if(nPatEmnt != 2) return(TCL_ERROR);
13909 patvar = Tcl_GetStringFromObj(objPatEmnt[0], NULL);
13910 patval = Tcl_GetStringFromObj(objPatEmnt[1], NULL);
13911 if(!patvar || !patval) return(TCL_ERROR);
13913 tstr = NULL;
13914 if(*patval){
13915 tstr = cpystr(patval);
13916 removing_leading_and_trailing_white_space(tstr);
13917 if(!(*tstr))
13918 fs_give((void **) &tstr);
13921 if(!(strcmp(patvar, "nickname"))){
13922 new_pat->patgrp->nick = tstr;
13923 tstr = NULL;
13925 else if(!(strcmp(patvar, "comment"))){
13926 new_pat->patgrp->comment = tstr;
13927 tstr = NULL;
13929 else if(!(strcmp(patvar, "to"))){
13930 new_pat->patgrp->to = string_to_pattern(tstr);
13932 else if(!(strcmp(patvar, "from"))){
13933 new_pat->patgrp->from = string_to_pattern(tstr);
13935 else if(!(strcmp(patvar, "sender"))){
13936 new_pat->patgrp->sender = string_to_pattern(tstr);
13938 else if(!(strcmp(patvar, "cc"))){
13939 new_pat->patgrp->cc = string_to_pattern(tstr);
13941 else if(!(strcmp(patvar, "recip"))){
13942 new_pat->patgrp->recip = string_to_pattern(tstr);
13944 else if(!(strcmp(patvar, "partic"))){
13945 new_pat->patgrp->partic = string_to_pattern(tstr);
13947 else if(!(strcmp(patvar, "news"))){
13948 new_pat->patgrp->news = string_to_pattern(tstr);
13950 else if(!(strcmp(patvar, "subj"))){
13951 new_pat->patgrp->subj = string_to_pattern(tstr);
13953 else if(!(strcmp(patvar, "bodytext"))){
13954 new_pat->patgrp->bodytext = string_to_pattern(tstr);
13956 else if(!(strcmp(patvar, "alltext"))){
13957 new_pat->patgrp->alltext = string_to_pattern(tstr);
13959 else if(!(strcmp(patvar, "keyword"))){
13960 new_pat->patgrp->keyword = string_to_pattern(tstr);
13962 else if(!(strcmp(patvar, "charset"))){
13963 new_pat->patgrp->charsets = string_to_pattern(tstr);
13965 else if(!(strcmp(patvar, "ftype"))){
13966 if(!tstr) return(TCL_ERROR);
13968 if(!(strcmp(tstr, "any")))
13969 new_pat->patgrp->fldr_type = FLDR_ANY;
13970 else if(!(strcmp(tstr, "news")))
13971 new_pat->patgrp->fldr_type = FLDR_NEWS;
13972 else if(!(strcmp(tstr, "email")))
13973 new_pat->patgrp->fldr_type = FLDR_EMAIL;
13974 else if(!(strcmp(tstr, "specific")))
13975 new_pat->patgrp->fldr_type = FLDR_SPECIFIC;
13976 else{
13977 free_pat(&new_pat);
13978 return(TCL_ERROR);
13981 else if(!(strcmp(patvar, "folder"))){
13982 new_pat->patgrp->folder = string_to_pattern(tstr);
13984 else if(!(strcmp(patvar, "stat_new"))){
13985 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_new)){
13986 free_pat(&new_pat);
13987 tcl_error++;
13990 else if(!(strcmp(patvar, "stat_rec"))){
13991 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_rec)){
13992 free_pat(&new_pat);
13993 tcl_error++;
13996 else if(!(strcmp(patvar, "stat_del"))){
13997 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_del)){
13998 free_pat(&new_pat);
13999 tcl_error++;
14002 else if(!(strcmp(patvar, "stat_imp"))){
14003 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_imp)){
14004 free_pat(&new_pat);
14005 tcl_error++;
14008 else if(!(strcmp(patvar, "stat_ans"))){
14009 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_ans)){
14010 free_pat(&new_pat);
14011 tcl_error++;
14014 else if(!(strcmp(patvar, "stat_8bitsubj"))){
14015 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_8bitsubj)){
14016 free_pat(&new_pat);
14017 tcl_error++;
14020 else if(!(strcmp(patvar, "stat_bom"))){
14021 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_bom)){
14022 free_pat(&new_pat);
14023 tcl_error++;
14026 else if(!(strcmp(patvar, "stat_boy"))){
14027 if(peRuleStatVal(tstr, &new_pat->patgrp->stat_boy)){
14028 free_pat(&new_pat);
14029 tcl_error++;
14032 else if(!(strcmp(patvar, "age"))){
14033 new_pat->patgrp->age = parse_intvl(tstr);
14035 else if(!(strcmp(patvar, "size"))){
14036 new_pat->patgrp->size = parse_intvl(tstr);
14038 else if(!(strcmp(patvar, "score"))){
14039 new_pat->patgrp->score = parse_intvl(tstr);
14041 else if(!(strcmp(patvar, "addrbook"))){
14042 if(tstr){
14043 if(!strcmp(tstr, "either"))
14044 new_pat->patgrp->inabook = IAB_EITHER;
14045 else if(!strcmp(tstr, "yes"))
14046 new_pat->patgrp->inabook = IAB_YES;
14047 else if(!strcmp(tstr, "no"))
14048 new_pat->patgrp->inabook = IAB_NO;
14049 else if(!strcmp(tstr, "yesspecific"))
14050 new_pat->patgrp->inabook = IAB_SPEC_YES;
14051 else if(!strcmp(tstr, "nospecific"))
14052 new_pat->patgrp->inabook = IAB_SPEC_NO;
14053 else
14054 tcl_error++;
14056 else
14057 tcl_error++;
14059 if(tcl_error)
14060 free_pat(&new_pat);
14062 else if(!(strcmp(patvar, "specificabook"))){
14063 new_pat->patgrp->abooks = string_to_pattern(tstr);
14065 else if(!(strcmp(patvar, "headers"))){
14066 ARBHDR_S **ahp;
14067 int nHdrList, nHdrPair, n;
14068 Tcl_Obj **objHdrList, **objHdrPair;
14070 Tcl_ListObjGetElements(interp, objPatEmnt[1], &nHdrList, &objHdrList);
14072 for(ahp = &new_pat->patgrp->arbhdr; *ahp; ahp = &(*ahp)->next)
14075 for (n = 0; n < nHdrList; n++){
14076 char *hdrfld;
14077 char *hdrval;
14079 Tcl_ListObjGetElements(interp, objHdrList[n], &nHdrPair, &objHdrPair);
14080 if(nHdrPair != 2)
14081 continue;
14083 hdrfld = Tcl_GetStringFromObj(objHdrPair[0], NULL);
14084 hdrval = Tcl_GetStringFromObj(objHdrPair[1], NULL);
14086 if(hdrfld){
14087 *ahp = (ARBHDR_S *) fs_get(sizeof(ARBHDR_S));
14088 memset(*ahp, 0, sizeof(ARBHDR_S));
14090 (*ahp)->field = cpystr(hdrfld);
14091 if(hdrval){
14092 (*ahp)->p = string_to_pattern(hdrval);
14094 else
14095 (*ahp)->isemptyval = 1;
14097 ahp = &(*ahp)->next;
14101 else{
14102 free_pat(&new_pat);
14103 tcl_error++;
14106 if(tstr)
14107 fs_give((void **) &tstr);
14109 if(tcl_error)
14110 return(TCL_ERROR);
14113 if((new_pat->patgrp->inabook & (IAB_SPEC_YES | IAB_SPEC_NO)) == 0
14114 && new_pat->patgrp->abooks)
14115 free_pattern(&new_pat->patgrp->abooks);
14117 if(new_pat->patgrp->fldr_type != FLDR_SPECIFIC && new_pat->patgrp->folder)
14118 free_pattern(&new_pat->patgrp->folder);
14120 /* set up the action */
14121 if(!(strcmp(rule, "filter")))
14122 new_pat->action->is_a_filter = 1;
14123 else if(!(strcmp(rule, "role")))
14124 new_pat->action->is_a_role = 1;
14125 else if(!(strcmp(rule, "score")))
14126 new_pat->action->is_a_score = 1;
14127 else if(!(strcmp(rule, "indexcolor")))
14128 new_pat->action->is_a_incol = 1;
14129 else{
14130 free_pat(&new_pat);
14131 return(TCL_ERROR);
14134 for(i = 0; i < nAct; i++){
14135 Tcl_ListObjGetElements(interp, objAct[i], &nActEmnt, &objActEmnt);
14136 if(nActEmnt !=2){
14137 free_pat(&new_pat);
14138 return(TCL_ERROR);
14141 actvar = Tcl_GetStringFromObj(objActEmnt[0], NULL);
14142 actval = Tcl_GetStringFromObj(objActEmnt[1], NULL);
14143 if(!actvar || !actval){
14144 free_pat(&new_pat);
14145 return(TCL_ERROR);
14148 if(new_pat->action->is_a_filter && !(strcmp(actvar, "action"))){
14149 if(!strcmp(actval, "delete"))
14150 new_pat->action->kill = 1;
14151 else if(!strcmp(actval, "move"))
14152 new_pat->action->kill = 0;
14153 else{
14154 free_pat(&new_pat);
14155 return(TCL_ERROR);
14158 else if(new_pat->action->is_a_filter && !(strcmp(actvar, "folder"))){
14159 tstr = cpystr(actval);
14160 removing_leading_and_trailing_white_space(tstr);
14161 if(!(*tstr)) fs_give((void **)&tstr);
14162 new_pat->action->folder = string_to_pattern(tstr);
14163 if(tstr) fs_give((void **)&tstr);
14165 else if(new_pat->action->is_a_filter && !(strcmp(actvar, "moind"))){
14166 if(!strcmp(actval, "1"))
14167 new_pat->action->move_only_if_not_deleted = 1;
14168 else if(!strcmp(actval, "0"))
14169 new_pat->action->move_only_if_not_deleted = 0;
14170 else{
14171 free_pat(&new_pat);
14172 return(TCL_ERROR);
14175 else if(new_pat->action->is_a_incol && !(strcmp(actvar, "fg"))){
14176 char asciicolor[256];
14178 if(ascii_colorstr(asciicolor, actval) == 0) {
14179 if(!new_pat->action->incol){
14180 new_pat->action->incol = new_color_pair(asciicolor,NULL);
14182 else
14183 snprintf(new_pat->action->incol->fg,
14184 sizeof(new_pat->action->incol->fg), "%s", asciicolor);
14187 else if(new_pat->action->is_a_incol && !(strcmp(actvar, "bg"))){
14188 char asciicolor[256];
14190 if(ascii_colorstr(asciicolor, actval) == 0) {
14191 if(!new_pat->action->incol){
14192 new_pat->action->incol = new_color_pair(NULL, asciicolor);
14194 else
14195 snprintf(new_pat->action->incol->bg,
14196 sizeof(new_pat->action->incol->bg), "%s", asciicolor);
14199 else if(new_pat->action->is_a_score && !(strcmp(actvar, "scoreval"))){
14200 long scoreval = (long) atoi(actval);
14202 if(scoreval >= SCORE_MIN && scoreval <= SCORE_MAX)
14203 new_pat->action->scoreval = scoreval;
14205 else if(new_pat->action->is_a_score && !(strcmp(actvar, "scorehdr"))){
14206 HEADER_TOK_S *hdrtok;
14208 if((hdrtok = stringform_to_hdrtok(actval)) != NULL)
14209 new_pat->action->scorevalhdrtok = hdrtok;
14211 else{
14212 free_pat(&new_pat);
14213 return(TCL_ERROR);
14217 if(new_pat->action->is_a_filter && new_pat->action->kill && new_pat->action->folder)
14218 fs_give((void **)&new_pat->action->folder);
14219 else if(new_pat->action->is_a_filter && new_pat->action->kill == 0 && new_pat->action->folder == 0){
14220 free_pat(&new_pat);
14221 Tcl_SetResult(interp, "No folder set for Move", TCL_VOLATILE);
14222 return(TCL_OK);
14226 if(aflags & RS_RULE_EDIT)
14227 rv = edit_pattern(new_pat, rno, rflags);
14228 else if(aflags & RS_RULE_ADD)
14229 rv = add_pattern(new_pat, rflags);
14230 else if(aflags & RS_RULE_DELETE)
14231 rv = delete_pattern(rno, rflags);
14232 else if(aflags & RS_RULE_SHUFFUP)
14233 rv = shuffle_pattern(rno, 1, rflags);
14234 else if(aflags & RS_RULE_SHUFFDOWN)
14235 rv = shuffle_pattern(rno, -1, rflags);
14236 else
14237 rv = 1;
14239 return(rv ? TCL_ERROR : TCL_OK);
14243 #if 0
14244 ADDRESS *
14245 peAEToAddress(AdrBk_Entry *ae)
14247 char *list, *l1, *l2;
14248 int length;
14249 BuildTo bldto;
14250 ADDRESS *addr = NULL;
14252 if(ae->tag == List){
14253 length = 0;
14254 for(l2 = ae->addr.list; *l2; l2++)
14255 length += (strlen(*l2) + 1);
14257 list = (char *) fs_get(length + 1);
14258 list[0] = '\0';
14259 l1 = list;
14260 for(l2 = ae->addr.list; *l2; l2++){
14261 if(l1 != list && l1-list < length+1)
14262 *l1++ = ',';
14264 strncpy(l1, *l2, length+1-(l1-list));
14265 l1 += strlen(l1);
14268 list[length] = '\0';
14270 bldto.type = Str;
14271 bldto.arg.str = list;
14272 adr2 = expand_address(bldto, userdomain, localdomain,
14273 loop_detected, fcc, did_set,
14274 lcc, error, 1, simple_verify,
14275 mangled);
14277 fs_give((void **) &list);
14279 else if(ae->tag == Single){
14280 if(strucmp(ae->addr.addr, a->mailbox)){
14281 bldto.type = Str;
14282 bldto.arg.str = ae->addr.addr;
14283 adr2 = expand_address(bldto, userdomain,
14284 localdomain, loop_detected,
14285 fcc, did_set, lcc,
14286 error, 1, simple_verify,
14287 mangled);
14289 else{
14291 * A loop within plain single entry is ignored.
14292 * Set up so later code thinks we expanded.
14294 adr2 = mail_newaddr();
14295 adr2->mailbox = cpystr(ae->addr.addr);
14296 adr2->host = cpystr(userdomain);
14297 adr2->adl = cpystr(a->adl);
14302 * Personal names: If the expanded address has a personal
14303 * name and the address book entry is a list with a fullname,
14304 * tack the full name from the address book on in front.
14305 * This mainly occurs with a distribution list where the
14306 * list has a full name, and the first person in the list also
14307 * has a full name.
14309 * This algorithm doesn't work very well if lists are
14310 * included within lists, but it's not clear what would
14311 * be better.
14313 if(ae->fullname && ae->fullname[0]){
14314 if(adr2->personal && adr2->personal[0]){
14315 if(ae->tag == List){
14316 /* combine list name and existing name */
14317 char *name;
14319 if(!simple_verify){
14320 size_t l;
14321 l = strlen(adr2->personal) + strlen(ae->fullname) + 4;
14322 name = (char *)fs_get((l+1) * sizeof(char));
14323 snprintf(name, l+1, "%s -- %s", ae->fullname,
14324 adr2->personal);
14325 fs_give((void **)&adr2->personal);
14326 adr2->personal = name;
14329 else{
14330 /* replace with nickname fullname */
14331 fs_give((void **)&adr2->personal);
14332 adr2->personal = adrbk_formatname(ae->fullname,
14333 NULL, NULL);
14336 else{
14337 if(abe-p>tag != List || !simple_verify){
14338 if(adr2->personal)
14339 fs_give((void **)&adr2->personal);
14341 adr2->personal = adrbk_formatname(abe->fullname,
14342 NULL, NULL);
14347 return(addr);
14352 char *
14353 peAEFcc(AdrBk_Entry *ae)
14355 char *fcc = NULL;
14357 if(ae->fcc && ae->fcc[0]){
14359 if(!strcmp(ae->fcc, "\"\""))
14360 fcc = cpystr("");
14361 else
14362 fcc = cpystr(ae->fcc);
14365 else if(ae->nickname && ae->nickname[0] &&
14366 (ps_global->fcc_rule == FCC_RULE_NICK ||
14367 ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
14369 * else if fcc-rule=fcc-by-nickname, use that
14372 fcc = cpystr(ae->nickname);
14375 return(fcc);
14377 #endif
14380 PINEFIELD *
14381 peCustomHdrs(void)
14383 extern PINEFIELD *parse_custom_hdrs(char **, CustomType);
14385 return(parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef));
14391 * PEClistCmd - Collection list editing tools
14394 PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
14396 char *err = "Unknown PEClist request";
14398 dprint((2, "PEClistCmd"));
14400 if(objc == 1){
14401 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
14403 else{
14404 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
14406 if(s1){
14407 if(objc == 3){ /* delete */
14408 if(!strcmp(s1, "delete")){
14409 int cl, i, n, deln;
14410 char **newl;
14411 CONTEXT_S *del_ctxt, *tmp_ctxt, *new_ctxt;
14413 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14414 Tcl_SetResult(interp,
14415 "cledit malformed: first arg must be int",
14416 TCL_VOLATILE);
14417 return(TCL_ERROR);
14419 for(i = 0, del_ctxt = ps_global->context_list;
14420 del_ctxt && i < cl; i++, del_ctxt = del_ctxt->next);
14421 if(!del_ctxt) return(TCL_ERROR);
14422 for(n = 0; del_ctxt->var.v->current_val.l[n]; n++);
14423 n--;
14424 newl = (char **) fs_get((n + 1) * sizeof(char *));
14425 newl[n] = NULL;
14426 deln = del_ctxt->var.i;
14427 for(i = 0; del_ctxt->var.v->current_val.l[i]; i++){
14428 if(i < deln)
14429 newl[i] = cpystr(del_ctxt->var.v->current_val.l[i]);
14430 else if(i > deln)
14431 newl[i-1] = cpystr(del_ctxt->var.v->current_val.l[i]);
14433 n = set_variable_list(del_ctxt->var.v - ps_global->vars,
14434 *newl ? newl : NULL, TRUE, Main);
14435 free_list_array(&newl);
14436 set_current_val(del_ctxt->var.v, TRUE, FALSE);
14437 if(n){
14438 Tcl_SetResult(interp,
14439 "Error saving changes",
14440 TCL_VOLATILE);
14441 return TCL_OK;
14443 for(tmp_ctxt = del_ctxt->next; tmp_ctxt && tmp_ctxt->var.v ==
14444 del_ctxt->var.v; tmp_ctxt = tmp_ctxt->next)
14445 tmp_ctxt->var.i--;
14446 if((tmp_ctxt = del_ctxt->next) != NULL)
14447 tmp_ctxt->prev = del_ctxt->prev;
14448 if((tmp_ctxt = del_ctxt->prev) != NULL)
14449 tmp_ctxt->next= del_ctxt->next;
14450 if(!del_ctxt->prev && !del_ctxt->next){
14451 new_ctxt = new_context(del_ctxt->var.v->current_val.l[0], NULL);
14452 ps_global->context_list = new_ctxt;
14453 if(!new_ctxt->var.v)
14454 new_ctxt->var = del_ctxt->var;
14456 else if(ps_global->context_list == del_ctxt){
14457 ps_global->context_list = del_ctxt->next;
14458 if(!ps_global->context_list)
14459 return TCL_ERROR; /* this shouldn't happen */
14461 if(ps_global->context_last == del_ctxt)
14462 ps_global->context_last = NULL;
14463 if(ps_global->context_current == del_ctxt){
14464 strncpy(ps_global->cur_folder,
14465 ps_global->mail_stream->mailbox,
14466 sizeof(ps_global->cur_folder));
14467 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
14468 ps_global->context_current = ps_global->context_list;
14470 del_ctxt->prev = NULL;
14471 del_ctxt->next = NULL;
14472 free_context(&del_ctxt);
14473 init_inbox_mapping(ps_global->VAR_INBOX_PATH,
14474 ps_global->context_list);
14475 return TCL_OK;
14477 else if(!strcmp(s1, "shuffdown")){
14478 int cl, i, shn, n;
14479 CONTEXT_S *sh_ctxt, *nsh_ctxt, *tctxt;
14480 char **newl, *tmpch;
14482 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14483 Tcl_SetResult(interp,
14484 "cledit malformed: first arg must be int",
14485 TCL_VOLATILE);
14486 return(TCL_ERROR);
14488 for(sh_ctxt = ps_global->context_list, i = 0;
14489 sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
14490 if(!sh_ctxt || !sh_ctxt->next){
14491 Tcl_SetResult(interp,
14492 "invalid context list number",
14493 TCL_VOLATILE);
14494 return TCL_ERROR;
14496 if(sh_ctxt->var.v == sh_ctxt->next->var.v){
14497 shn = sh_ctxt->var.i;
14498 for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14499 newl = (char **) fs_get((n + 1) * sizeof(char *));
14500 newl[n] = NULL;
14501 for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
14502 if(i == shn)
14503 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
14504 else if(i == shn + 1)
14505 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
14506 else
14507 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14509 n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
14510 newl, TRUE, Main);
14511 free_list_array(&newl);
14512 set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14513 if(n){
14514 Tcl_SetResult(interp,
14515 "Error saving changes",
14516 TCL_VOLATILE);
14517 return TCL_OK;
14519 nsh_ctxt = sh_ctxt->next;
14520 nsh_ctxt->var.i--;
14521 sh_ctxt->var.i++;
14523 else{
14524 nsh_ctxt = sh_ctxt->next;
14525 shn = sh_ctxt->var.i;
14526 tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
14527 for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14528 n--;
14529 newl = (char **) fs_get((n + 1) * sizeof(char *));
14530 newl[n] = NULL;
14531 for(i = 0; sh_ctxt->var.v->current_val.l[i+1]; i++)
14532 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14533 n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
14534 newl, FALSE, Main);
14535 free_list_array(&newl);
14536 set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14537 for(n = 0; nsh_ctxt->var.v->current_val.l[n]; n++);
14538 n++;
14539 newl = (char **) fs_get((n + 1) * sizeof(char *));
14540 newl[n] = NULL;
14541 newl[0] = cpystr(nsh_ctxt->var.v->current_val.l[0]);
14542 newl[1] = tmpch;
14543 for(i = 2; nsh_ctxt->var.v->current_val.l[i-1]; i++)
14544 newl[i] = cpystr(nsh_ctxt->var.v->current_val.l[i-1]);
14545 n = set_variable_list(nsh_ctxt->var.v - ps_global->vars,
14546 newl, TRUE, Main);
14547 free_list_array(&newl);
14548 set_current_val(nsh_ctxt->var.v, TRUE, FALSE);
14549 sh_ctxt->var.v = nsh_ctxt->var.v;
14550 sh_ctxt->var.i = 1;
14551 /* this for loop assumes that there are only two variable lists,
14552 * folder-collections and news-collections, a little more will
14553 * have to be done if we want to accomodate for the INHERIT
14554 * option introduced in 4.30.
14556 for(tctxt = nsh_ctxt->next; tctxt; tctxt = tctxt->next)
14557 tctxt->var.i++;
14559 if(sh_ctxt->prev) sh_ctxt->prev->next = nsh_ctxt;
14560 nsh_ctxt->prev = sh_ctxt->prev;
14561 sh_ctxt->next = nsh_ctxt->next;
14562 nsh_ctxt->next = sh_ctxt;
14563 sh_ctxt->prev = nsh_ctxt;
14564 if(sh_ctxt->next) sh_ctxt->next->prev = sh_ctxt;
14565 if(ps_global->context_list == sh_ctxt)
14566 ps_global->context_list = nsh_ctxt;
14567 init_inbox_mapping(ps_global->VAR_INBOX_PATH,
14568 ps_global->context_list);
14569 return TCL_OK;
14571 else if(!strcmp(s1, "shuffup")){
14572 int cl, i, shn, n;
14573 CONTEXT_S *sh_ctxt, *psh_ctxt, *tctxt;
14574 char **newl, *tmpch;
14576 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14577 Tcl_SetResult(interp,
14578 "cledit malformed: first arg must be int",
14579 TCL_VOLATILE);
14580 return(TCL_ERROR);
14582 for(sh_ctxt = ps_global->context_list, i = 0;
14583 sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
14584 if(!sh_ctxt || !sh_ctxt->prev){
14585 Tcl_SetResult(interp,
14586 "invalid context list number",
14587 TCL_VOLATILE);
14588 return TCL_ERROR;
14590 if(sh_ctxt->var.v == sh_ctxt->prev->var.v){
14591 shn = sh_ctxt->var.i;
14592 for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14593 newl = (char **) fs_get((n + 1) * sizeof(char *));
14594 newl[n] = NULL;
14595 for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
14596 if(i == shn)
14597 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
14598 else if(i == shn - 1)
14599 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
14600 else
14601 newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14603 i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
14604 newl, TRUE, Main);
14605 free_list_array(&newl);
14606 set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14607 if(i){
14608 Tcl_SetResult(interp,
14609 "Error saving changes",
14610 TCL_VOLATILE);
14611 return TCL_OK;
14613 psh_ctxt = sh_ctxt->prev;
14614 psh_ctxt->var.i++;
14615 sh_ctxt->var.i--;
14617 else{
14618 psh_ctxt = sh_ctxt->prev;
14619 shn = sh_ctxt->var.i;
14620 tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
14621 for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
14622 n--;
14623 newl = (char **) fs_get((n + 1) * sizeof(char *));
14624 newl[n] = NULL;
14625 for(i = 1; sh_ctxt->var.v->current_val.l[i]; i++)
14626 newl[i-1] = cpystr(sh_ctxt->var.v->current_val.l[i]);
14627 i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
14628 newl, FALSE, Main);
14629 free_list_array(&newl);
14630 if(i){
14631 Tcl_SetResult(interp,
14632 "Error saving changes",
14633 TCL_VOLATILE);
14634 return TCL_OK;
14636 set_current_val(sh_ctxt->var.v, TRUE, FALSE);
14637 for(n = 0; psh_ctxt->var.v->current_val.l[n]; n++);
14638 n++;
14639 newl = (char **) fs_get((n + 1) * sizeof(char *));
14640 newl[n] = NULL;
14641 for(i = 0; psh_ctxt->var.v->current_val.l[i+1]; i++)
14642 newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i]);
14643 newl[i++] = tmpch;
14644 newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i-1]);
14645 i = set_variable_list(psh_ctxt->var.v - ps_global->vars,
14646 newl, TRUE, Main);
14647 free_list_array(&newl);
14648 if(i){
14649 Tcl_SetResult(interp,
14650 "Error saving changes",
14651 TCL_VOLATILE);
14652 return TCL_OK;
14654 set_current_val(psh_ctxt->var.v, TRUE, FALSE);
14655 for(tctxt = sh_ctxt->next ; tctxt; tctxt = tctxt->next)
14656 tctxt->var.i--;
14657 sh_ctxt->var.v = psh_ctxt->var.v;
14658 sh_ctxt->var.i = n - 2;
14659 /* There MUST be at least 2 collections in the list */
14660 psh_ctxt->var.i++;
14662 if(sh_ctxt->next) sh_ctxt->next->prev = psh_ctxt;
14663 psh_ctxt->next = sh_ctxt->next;
14664 sh_ctxt->prev = psh_ctxt->prev;
14665 psh_ctxt->prev = sh_ctxt;
14666 sh_ctxt->next = psh_ctxt;
14667 if(sh_ctxt->prev) sh_ctxt->prev->next = sh_ctxt;
14668 if(ps_global->context_list == psh_ctxt)
14669 ps_global->context_list = sh_ctxt;
14670 init_inbox_mapping(ps_global->VAR_INBOX_PATH,
14671 ps_global->context_list);
14672 return TCL_OK;
14675 else if(objc == 7){
14676 if(!strcmp(s1, "edit") || !strcmp(s1, "add")){
14677 int cl, quotes_needed = 0, i, add = 0, n = 0;
14678 char *nick, *server, *path, *view,
14679 context_buf[MAILTMPLEN*4], **newl;
14680 CONTEXT_S *new_ctxt, *tmp_ctxt;
14682 if(!strcmp(s1, "add")) add = 1;
14684 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
14685 Tcl_SetResult(interp,
14686 "cledit malformed: first arg must be int",
14687 TCL_VOLATILE);
14688 return(TCL_ERROR);
14690 if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))){
14691 Tcl_SetResult(interp,
14692 "Error1",
14693 TCL_VOLATILE);
14694 return TCL_ERROR;
14696 if(!(server = Tcl_GetStringFromObj(objv[4], NULL))){
14697 Tcl_SetResult(interp,
14698 "Error2",
14699 TCL_VOLATILE);
14700 return TCL_ERROR;
14702 if(!(path = Tcl_GetStringFromObj(objv[5], NULL))){
14703 Tcl_SetResult(interp,
14704 "Error3",
14705 TCL_VOLATILE);
14706 return TCL_ERROR;
14708 if(!(view = Tcl_GetStringFromObj(objv[6], NULL))){
14709 Tcl_SetResult(interp,
14710 "Error4",
14711 TCL_VOLATILE);
14712 return TCL_ERROR;
14714 removing_leading_and_trailing_white_space(nick);
14715 removing_leading_and_trailing_white_space(server);
14716 removing_leading_and_trailing_white_space(path);
14717 removing_leading_and_trailing_white_space(view);
14718 if(strchr(nick, ' '))
14719 quotes_needed = 1;
14720 if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
14721 MAILTMPLEN * 4 - 20) { /* for good measure */
14722 Tcl_SetResult(interp,
14723 "info too long",
14724 TCL_VOLATILE);
14726 return TCL_ERROR;
14728 if(3 + strlen(nick) + strlen(server) + strlen(path) +
14729 strlen(view) > MAILTMPLEN + 4){
14730 Tcl_SetResult(interp,
14731 "collection fields too long",
14732 TCL_VOLATILE);
14733 return(TCL_OK);
14735 snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
14736 "\"" : "", nick, quotes_needed ? "\"" : "",
14737 strlen(nick) ? " " : "",
14738 server, path, view);
14739 new_ctxt = new_context(context_buf, NULL);
14740 if(!add){
14741 for(tmp_ctxt = ps_global->context_list, i = 0;
14742 tmp_ctxt && i < cl; i++, tmp_ctxt = tmp_ctxt->next);
14743 if(!tmp_ctxt){
14744 Tcl_SetResult(interp,
14745 "invalid context list number",
14746 TCL_VOLATILE);
14747 return TCL_ERROR;
14749 new_ctxt->next = tmp_ctxt->next;
14750 new_ctxt->prev = tmp_ctxt->prev;
14751 if(tmp_ctxt->prev && tmp_ctxt->prev->next == tmp_ctxt)
14752 tmp_ctxt->prev->next = new_ctxt;
14753 if(tmp_ctxt->next && tmp_ctxt->next->prev == tmp_ctxt)
14754 tmp_ctxt->next->prev = new_ctxt;
14755 if(ps_global->context_list == tmp_ctxt)
14756 ps_global->context_list = new_ctxt;
14757 if(ps_global->context_current == tmp_ctxt){
14758 strncpy(ps_global->cur_folder,
14759 ps_global->mail_stream->mailbox,
14760 sizeof(ps_global->cur_folder));
14761 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
14762 ps_global->context_current = new_ctxt;
14764 if(ps_global->context_last == tmp_ctxt)
14765 ps_global->context_last = new_ctxt;
14766 new_ctxt->var = tmp_ctxt->var;
14767 tmp_ctxt->next = tmp_ctxt->prev = NULL;
14768 free_context(&tmp_ctxt);
14770 else {
14771 for(tmp_ctxt = ps_global->context_list;
14772 tmp_ctxt->next; tmp_ctxt = tmp_ctxt->next);
14773 new_ctxt->prev = tmp_ctxt;
14774 tmp_ctxt->next = new_ctxt;
14775 new_ctxt->var.v = tmp_ctxt->var.v;
14776 new_ctxt->var.i = tmp_ctxt->var.i + 1;
14778 if(!new_ctxt->var.v){
14779 Tcl_SetResult(interp,
14780 "Error5",
14781 TCL_VOLATILE);
14782 return TCL_ERROR;
14784 for(n = 0; new_ctxt->var.v->current_val.l[n]; n++);
14785 if(add) n++;
14786 newl = (char **) fs_get((n + 1) * sizeof(char *));
14787 newl[n] = NULL;
14788 for(n = 0; new_ctxt->var.v->current_val.l[n]; n++)
14789 newl[n] = (n == new_ctxt->var.i)
14790 ? cpystr(context_buf)
14791 : cpystr(new_ctxt->var.v->current_val.l[n]);
14792 if(add) newl[n++] = cpystr(context_buf);
14793 n = set_variable_list(new_ctxt->var.v - ps_global->vars,
14794 newl, TRUE, Main);
14795 free_list_array(&newl);
14796 set_current_val(new_ctxt->var.v, TRUE, FALSE);
14797 init_inbox_mapping(ps_global->VAR_INBOX_PATH,
14798 ps_global->context_list);
14799 if(n){
14800 Tcl_SetResult(interp,
14801 "Error saving changes",
14802 TCL_VOLATILE);
14803 return TCL_OK;
14805 return TCL_OK;
14811 Tcl_SetResult(interp, err, TCL_STATIC);
14812 return(TCL_ERROR);
14817 * peTakeaddr - Take Address
14820 peTakeaddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
14822 TA_S *talist = NULL, *current, *head;
14823 Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
14824 int anum = 0;
14826 mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
14828 if(set_up_takeaddr('a', ps_global, sp_msgmap(ps_global->mail_stream),
14829 &talist, &anum, TA_NOPROMPT, NULL) < 0
14830 || (talist == NULL)){
14831 Tcl_SetResult(interp,
14832 "Take address failed to set up",
14833 TCL_VOLATILE);
14834 return(TCL_ERROR);
14837 for(head = talist ; head->prev; head = head->prev);
14839 * Return value will be of the form:
14841 * { "line to print",
14842 * {"personal", "mailbox", "host"} # addr
14843 * {"nick", "fullname", "fcc", "comment"} # suggested
14845 * ...
14848 * The two list items will be empty if that line is
14849 * just informational.
14851 itemObj = Tcl_NewListObj(0, NULL);
14852 for(current = head; current ; current = current->next){
14853 if(current->skip_it && !current->print) continue;
14854 secObj = Tcl_NewListObj(0, NULL);
14855 if(Tcl_ListObjAppendElement(interp, secObj,
14856 Tcl_NewStringObj(current->strvalue,-1)) != TCL_OK)
14857 return(TCL_ERROR);
14858 resObj = Tcl_NewListObj(0, NULL);
14859 /* append the address information */
14860 if(current->addr && !current->print){
14861 if(Tcl_ListObjAppendElement(interp, resObj,
14862 Tcl_NewStringObj(current->addr->personal
14863 ? current->addr->personal
14864 : "", -1)) != TCL_OK)
14865 return(TCL_ERROR);
14866 if(Tcl_ListObjAppendElement(interp, resObj,
14867 Tcl_NewStringObj(current->addr->mailbox
14868 ? current->addr->mailbox
14869 : "", -1)) != TCL_OK)
14870 return(TCL_ERROR);
14871 if(Tcl_ListObjAppendElement(interp, resObj,
14872 Tcl_NewStringObj(current->addr->host
14873 ? current->addr->host
14874 : "", -1)) != TCL_OK)
14875 return(TCL_ERROR);
14877 if(Tcl_ListObjAppendElement(interp, secObj,
14878 resObj) != TCL_OK)
14879 return(TCL_ERROR);
14880 resObj = Tcl_NewListObj(0, NULL);
14881 /* append the suggested possible entries */
14882 if(!current->print
14883 && (current->nickname || current->fullname
14884 || current->fcc || current->comment)){
14885 if(Tcl_ListObjAppendElement(interp, resObj,
14886 Tcl_NewStringObj(current->nickname
14887 ? current->nickname
14888 : "", -1)) != TCL_OK)
14889 return(TCL_ERROR);
14890 if(Tcl_ListObjAppendElement(interp, resObj,
14891 Tcl_NewStringObj(current->fullname
14892 ? current->fullname
14893 : "", -1)) != TCL_OK)
14894 return(TCL_ERROR);
14895 if(Tcl_ListObjAppendElement(interp, resObj,
14896 Tcl_NewStringObj(current->fcc
14897 ? current->fcc
14898 : "", -1)) != TCL_OK)
14899 return(TCL_ERROR);
14900 if(Tcl_ListObjAppendElement(interp, resObj,
14901 Tcl_NewStringObj(current->comment
14902 ? current->comment
14903 : "", -1)) != TCL_OK)
14904 return(TCL_ERROR);
14906 if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
14907 return(TCL_ERROR);
14908 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
14909 secObj) != TCL_OK)
14910 return(TCL_ERROR);
14913 free_talines(&talist);
14914 return(TCL_OK);
14919 * peTakeFrom - Take only From Address
14922 peTakeFrom(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
14924 char *err = NULL;
14925 Tcl_Obj *objItem;
14926 ADDRESS *ap;
14927 ENVELOPE *env;
14928 long rawno;
14931 * Return value will be of the form:
14933 * { "line to print",
14934 * {"personal", "mailbox", "host"} # addr
14935 * {"nick", "fullname", "fcc", "comment"} # suggested
14937 * ...
14940 * The two list items will be empty if that line is
14941 * just informational.
14944 if(uid){
14945 if((env = pine_mail_fetchstructure(ps_global->mail_stream,
14946 rawno = peSequenceNumber(uid),
14947 NULL))){
14948 /* append the address information */
14949 for(ap = env->from; ap; ap = ap->next){
14950 objItem = Tcl_NewListObj(0, NULL);
14951 /* append EMPTY "line to print" */
14952 if(Tcl_ListObjAppendElement(interp, objItem, Tcl_NewStringObj("",-1)) != TCL_OK)
14953 return(TCL_ERROR);
14955 /* append address info */
14956 peAppListF(interp, objItem, "%s%s%s",
14957 ap->personal ? (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, ap->personal) : "",
14958 ap->mailbox ? ap->mailbox : "",
14959 ap->host ? ap->host : "");
14961 /* append suggested info */
14962 peAddSuggestedContactInfo(interp, objItem, ap);
14964 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objItem) != TCL_OK)
14965 return(TCL_ERROR);
14968 return(TCL_OK);
14970 else
14971 err = ps_global->last_error;
14973 else
14974 err = "Invalid UID";
14976 return(TCL_ERROR);
14981 peAddSuggestedContactInfo(Tcl_Interp *interp, Tcl_Obj *lobjp, ADDRESS *addr)
14983 char *nick = NULL, *full = NULL, *fcc = NULL, *comment = NULL;
14985 get_contactinfo_from_addr(addr, &nick, &full, &fcc, &comment);
14987 peAppListF(interp, lobjp, "%s%s%s%s",
14988 nick ? nick : "",
14989 full ? full : "",
14990 fcc ? fcc : "",
14991 comment ? comment : "");
14993 if(nick)
14994 fs_give((void **) &nick);
14996 if(full)
14997 fs_give((void **) &full);
14999 if(fcc)
15000 fs_give((void **) &fcc);
15002 if(comment)
15003 fs_give((void **) &comment);
15005 return 0;
15009 /* * * * * * * * * Status message ring management * * * * * * * * * * * * */
15011 STATMSG_S *
15012 sml_newmsg(int priority, char *text)
15014 static long id = 1;
15015 STATMSG_S *smp;
15017 smp = (STATMSG_S *) fs_get(sizeof(STATMSG_S));
15018 memset(smp, 0, sizeof(STATMSG_S));
15019 smp->id = id++;
15020 smp->posted = time(0);
15021 smp->type = priority;
15022 smp->text = cpystr(text);
15023 return(smp);
15027 void
15028 sml_addmsg(int priority, char *text)
15030 STATMSG_S *smp = sml_newmsg(priority, text);
15032 if(peStatList){
15033 smp->next = peStatList;
15034 peStatList = smp;
15036 else
15037 peStatList = smp;
15041 char **
15042 sml_getmsgs(void)
15044 int n;
15045 STATMSG_S *smp;
15046 char **retstrs = NULL, **tmpstrs;
15048 for(n = 0, smp = peStatList; smp && !smp->seen; n++, smp = smp->next)
15051 if(n == 0) return NULL;
15052 retstrs = (char **)fs_get((n+1)*sizeof(char *));
15053 for(tmpstrs = retstrs, smp = peStatList; smp && !smp->seen; smp = smp->next){
15054 *tmpstrs = smp->text;
15055 tmpstrs++;
15058 *tmpstrs = NULL;
15059 return(retstrs);
15063 char *
15064 sml_getmsg(void)
15066 return(peStatList ? peStatList->text : "");
15069 void
15070 sml_seen(void)
15072 STATMSG_S *smp;
15074 for(smp = peStatList; smp; smp = smp->next)
15075 smp->seen = 1;
15080 /* * * * * * * * * LDAP Support Routines * * * * * * * * * * * */
15084 * PELdapCmd - LDAP TCL interface
15087 PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
15089 #ifndef ENABLE_LDAP
15090 char *err = "Call to PELdap when LDAP not enabled";
15091 #else
15092 char *err = "Unknown PELdap request";
15093 char *s1;
15095 dprint((2, "PELdapCmd"));
15097 if(objc == 1){
15098 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15099 Tcl_SetResult(interp, err, TCL_STATIC);
15100 return(TCL_ERROR);
15102 s1 = Tcl_GetStringFromObj(objv[1], NULL);
15104 if(s1){
15105 int qn;
15106 if(!strcmp(s1, "directories")){
15107 int i;
15108 LDAP_SERV_S *info;
15109 Tcl_Obj *secObj;
15111 if(objc != 2){
15112 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15113 Tcl_SetResult(interp, err, TCL_STATIC);
15114 return(TCL_ERROR);
15116 if(ps_global->VAR_LDAP_SERVERS){
15117 for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
15118 ps_global->VAR_LDAP_SERVERS[i][0]; i++){
15119 info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
15120 secObj = Tcl_NewListObj(0, NULL);
15121 if(Tcl_ListObjAppendElement(interp, secObj,
15122 Tcl_NewStringObj(info->nick ? info->nick
15123 : "", -1)) != TCL_OK)
15124 return(TCL_ERROR);
15125 if(Tcl_ListObjAppendElement(interp, secObj,
15126 Tcl_NewStringObj(info->serv ? info->serv
15127 : "", -1)) != TCL_OK)
15128 return(TCL_ERROR);
15130 if(info)
15131 free_ldap_server_info(&info);
15132 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15133 secObj) != TCL_OK)
15134 return(TCL_ERROR);
15137 else
15138 Tcl_SetResult(interp, "", TCL_STATIC);
15140 return(TCL_OK);
15142 else if(!strcmp(s1, "query")){
15143 int dir;
15144 char *srchstr, *filtstr;
15145 LDAP_CHOOSE_S *winning_e = NULL;
15146 LDAP_SERV_RES_S *results = NULL;
15147 WP_ERR_S wp_err;
15148 CUSTOM_FILT_S *filter = NULL;
15150 if(objc != 5){
15151 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15152 Tcl_SetResult(interp, err, TCL_STATIC);
15153 return(TCL_ERROR);
15155 if(Tcl_GetIntFromObj(interp, objv[2], &dir) == TCL_ERROR){
15156 Tcl_SetResult(interp,
15157 "PELdap results malformed: first arg must be int",
15158 TCL_VOLATILE);
15159 return(TCL_ERROR);
15161 wpldap_global->query_no++;
15162 if(wpldap_global->ldap_search_list){
15163 wpldap_global->ldap_search_list =
15164 free_wpldapres(wpldap_global->ldap_search_list);
15166 srchstr = Tcl_GetStringFromObj(objv[3], NULL);
15167 filtstr = Tcl_GetStringFromObj(objv[4], NULL);
15168 if(!srchstr) return(TCL_ERROR);
15169 if(!filtstr) return(TCL_ERROR);
15170 if(*filtstr){
15171 filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
15172 filter->filt = cpystr(filtstr);
15173 filter->combine = 0;
15175 memset(&wp_err, 0, sizeof(wp_err));
15176 ldap_lookup_all(srchstr, dir, 0, AlwaysDisplay, filter, &winning_e,
15177 &wp_err, &results);
15178 if(filter){
15179 fs_give((void **)&filter->filt);
15180 fs_give((void **)&filter);
15182 Tcl_SetResult(interp, int2string(wpldap_global->ldap_search_list
15183 ? wpldap_global->query_no : 0),
15184 TCL_VOLATILE);
15185 return(TCL_OK);
15188 * First argument has always got to be the query number for now.
15189 * Might need to rething that when setting up queries.
15191 if(objc == 2){
15192 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
15193 Tcl_SetResult(interp, err, TCL_STATIC);
15194 return(TCL_ERROR);
15196 if(Tcl_GetIntFromObj(interp, objv[2], &qn) == TCL_ERROR){
15197 Tcl_SetResult(interp,
15198 "PELdap results malformed: first arg must be int",
15199 TCL_VOLATILE);
15200 return(TCL_ERROR);
15202 if(qn != wpldap_global->query_no){
15203 Tcl_SetResult(interp,
15204 "Query is no longer valid", TCL_VOLATILE);
15205 return(TCL_ERROR);
15207 if(objc == 3){
15208 if(!strcmp(s1, "results")){
15209 return(peLdapQueryResults(interp));
15212 else if(objc == 4){
15213 if(!strcmp(s1, "ldapext")){
15215 * Returns a list of the form:
15216 * {"dn" {{attrib {val, ...}}, ...}}
15218 char *whichrec = Tcl_GetStringFromObj(objv[3], NULL);
15219 char *tmpstr, *tmp, *tmp2, *a;
15220 struct berval **vals;
15221 WPLDAPRES_S *curres;
15222 LDAP_CHOOSE_S *winning_e = NULL;
15223 LDAP_SERV_RES_S *trl;
15224 Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
15225 BerElement *ber;
15226 LDAPMessage *e;
15227 int i, j, whichi, whichj;
15229 if(whichrec == NULL){
15230 Tcl_SetResult(interp, "Ldap ldapext error 1", TCL_VOLATILE);
15231 return TCL_ERROR;
15233 tmpstr = cpystr(whichrec);
15234 tmp = tmpstr;
15235 for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15236 if(*tmp2 != '.'){
15237 Tcl_SetResult(interp, "Ldap ldapext error 2", TCL_VOLATILE);
15238 return TCL_ERROR;
15240 *tmp2 = '\0';
15241 whichi = atoi(tmp);
15242 *tmp2 = '.';
15243 tmp2++;
15244 for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15245 if(*tmp2 != '\0'){
15246 Tcl_SetResult(interp, "Ldap ldapext error 3", TCL_VOLATILE);
15247 return TCL_ERROR;
15249 whichj = atoi(tmp);
15250 fs_give((void **)&tmpstr);
15251 for(curres = wpldap_global->ldap_search_list, i = 0;
15252 i < whichi && curres; i++, curres = curres->next);
15253 if(!curres){
15254 Tcl_SetResult(interp, "Ldap ldapext error 4", TCL_VOLATILE);
15255 return TCL_ERROR;
15257 for(trl = curres->reslist, j = 0; trl; trl = trl->next){
15258 for(e = ldap_first_entry(trl->ld, trl->res);
15259 e != NULL && j < whichj;
15260 e = ldap_next_entry(trl->ld, e), j++);
15261 if(e != NULL && j == whichj)
15262 break;
15264 if(e == NULL || trl == NULL){
15265 Tcl_SetResult(interp, "Ldap ldapext error 5", TCL_VOLATILE);
15266 return TCL_ERROR;
15268 winning_e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
15269 winning_e->ld = trl->ld;
15270 winning_e->selected_entry = e;
15271 winning_e->info_used = trl->info_used;
15272 winning_e->serv = trl->serv;
15273 a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
15274 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15275 Tcl_NewStringObj(a ? a : "", -1)) != TCL_OK)
15276 return(TCL_ERROR);
15277 if(a)
15278 our_ldap_dn_memfree(a);
15280 itemObj = Tcl_NewListObj(0, NULL);
15281 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
15282 a != NULL;
15283 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
15284 if(a && *a){
15285 secObj = Tcl_NewListObj(0, NULL);
15286 if(Tcl_ListObjAppendElement(interp, secObj,
15287 Tcl_NewStringObj(ldap_translate(a,
15288 winning_e->info_used), -1)) != TCL_OK)
15289 return(TCL_ERROR);
15290 resObj = Tcl_NewListObj(0, NULL);
15291 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
15292 if(vals){
15293 for(i = 0; vals[i]; i++){
15294 if(Tcl_ListObjAppendElement(interp, resObj,
15295 Tcl_NewStringObj(vals[i]->bv_val, -1)) != TCL_OK)
15296 return(TCL_ERROR);
15298 ldap_value_free_len(vals);
15299 if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15300 return(TCL_ERROR);
15302 if(!strcmp(a,"objectclass")){
15303 if(Tcl_ListObjAppendElement(interp, secObj,
15304 Tcl_NewStringObj("objectclass", -1)) != TCL_OK)
15305 return(TCL_ERROR);
15307 if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK)
15308 return(TCL_ERROR);
15310 our_ldap_memfree(a);
15313 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15314 itemObj) != TCL_OK)
15315 return(TCL_ERROR);
15317 fs_give((void **)&winning_e);
15318 return(TCL_OK);
15321 else if(objc == 6){
15322 if(!strcmp(s1, "setaddrs")){
15323 char *listset = Tcl_GetStringFromObj(objv[3], NULL);
15324 char *addrstr = Tcl_GetStringFromObj(objv[4], NULL);
15325 char *tmp, *tmp2, *tmplistset, was_char, *ret_to,
15326 *tmpaddrstr;
15327 int **lset, noreplace = 0;
15328 ADDRESS *adr = NULL, *curadr, *prevadr, *newadr,
15329 *curnewadr, *newadrs;
15330 int curi, i, j, numsrchs, numset, setit;
15331 LDAP_CHOOSE_S *tres;
15332 LDAP_SERV_RES_S *trl;
15333 WPLDAPRES_S *curres;
15334 LDAPMessage *e;
15335 RFC822BUFFER rbuf;
15336 size_t len;
15338 if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){
15339 Tcl_SetResult(interp,
15340 "PELdap results malformed: first arg must be int",
15341 TCL_VOLATILE);
15342 return(TCL_ERROR);
15344 if(listset == NULL || addrstr == NULL) return TCL_ERROR;
15345 tmpaddrstr = cpystr(addrstr);
15347 if(!noreplace){
15348 mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
15349 rfc822_parse_adrlist(&adr, tmpaddrstr, "@");
15350 mail_parameters(NIL, SET_PARSEPHRASE, NULL);
15353 tmplistset = cpystr(listset);
15354 for(curres = wpldap_global->ldap_search_list, numsrchs = 0;
15355 curres; curres = curres->next, numsrchs++);
15356 lset = (int **)fs_get((numsrchs+1)*sizeof(int *));
15357 for(i = 0; i < numsrchs; i++){
15358 for(tmp = tmplistset, numset = 0; *tmp;){
15359 for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15360 if(*tmp2 != '.'){
15361 Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE);
15362 return TCL_ERROR;
15364 if(atoi(tmp) == i) numset++;
15365 tmp2++;
15366 for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15367 if(*tmp2 != ',' && *tmp2 != '\0'){
15368 Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE);
15369 return TCL_ERROR;
15371 if(*tmp2) tmp2++;
15372 tmp = tmp2;
15374 lset[i] = (int *)fs_get((numset+1)*sizeof(int));
15375 for(tmp = tmplistset, j = 0; *tmp && j < numset;){
15376 setit = 0;
15377 for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15378 if(*tmp2 != '.'){
15379 Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE);
15380 return TCL_ERROR;
15382 *tmp2 = '\0';
15383 if(atoi(tmp) == i) setit++;
15384 *tmp2 = '.';
15385 tmp2++;
15386 for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15387 if(*tmp2 != ',' && *tmp2 != '\0'){
15388 Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE);
15389 return TCL_ERROR;
15391 if(setit){
15392 was_char = *tmp2;
15393 *tmp2 = '\0';
15394 lset[i][j++] = atoi(tmp);
15395 *tmp2 = was_char;
15397 if(*tmp2) tmp2++;
15398 tmp = tmp2;
15400 lset[i][j] = -1;
15402 lset[i] = NULL;
15403 for(i = 0, curres = wpldap_global->ldap_search_list;
15404 i < numsrchs && curres; i++, curres = curres->next){
15405 prevadr = NULL;
15406 for(curadr = adr; curadr; curadr = curadr->next){
15407 if(strcmp(curadr->mailbox, curres->str) == 0
15408 && curadr->host && *curadr->host == '@')
15409 break;
15410 prevadr = curadr;
15412 if(!curadr && !noreplace){
15413 Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE);
15414 return TCL_ERROR;
15416 newadrs = newadr = curnewadr = NULL;
15417 for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){
15418 for(e = ldap_first_entry(trl->ld, trl->res);
15419 e != NULL && lset[i][curi] != -1;
15420 e = ldap_next_entry(trl->ld, e), j++){
15421 if(j == lset[i][curi]){
15422 tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
15423 tres->ld = trl->ld;
15424 tres->selected_entry = e;
15425 tres->info_used = trl->info_used;
15426 tres->serv = trl->serv;
15427 newadr = address_from_ldap(tres);
15428 fs_give((void **)&tres);
15430 if(newadrs == NULL){
15431 newadrs = curnewadr = newadr;
15433 else {
15434 curnewadr->next = newadr;
15435 curnewadr = newadr;
15437 curi++;
15441 if(newadrs == NULL || curnewadr == NULL){
15442 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname");
15443 q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
15444 newadr = copyaddr(curadr);
15445 if(newadrs == NULL){
15446 newadrs = curnewadr = newadr;
15448 else {
15449 curnewadr->next = newadr;
15450 curnewadr = newadr;
15453 curnewadr->next = curadr ? curadr->next : NULL;
15454 if(curadr) curadr->next = NULL;
15455 if(curadr == adr)
15456 adr = newadrs;
15457 else{
15458 prevadr->next = newadrs;
15459 if(curadr)
15460 mail_free_address(&curadr);
15464 len = est_size(adr);
15465 ret_to = (char *)fs_get(len * sizeof(char));
15466 ret_to[0] = '\0';
15467 strip_personal_quotes(adr);
15468 rbuf.f = dummy_soutr;
15469 rbuf.s = NULL;
15470 rbuf.beg = ret_to;
15471 rbuf.cur = ret_to;
15472 rbuf.end = ret_to+len-1;
15473 rfc822_output_address_list(&rbuf, adr, 0L, NULL);
15474 *rbuf.cur = '\0';
15475 Tcl_SetResult(interp, ret_to, TCL_VOLATILE);
15476 fs_give((void **)&ret_to);
15477 fs_give((void **)&tmpaddrstr);
15478 fs_give((void **)&tmplistset);
15479 for(i = 0; lset[i]; i++)
15480 fs_give((void **)&lset[i]);
15481 fs_give((void **)&lset);
15482 if(adr)
15483 mail_free_address(&adr);
15485 return(TCL_OK);
15489 #endif /* ENABLE_LDAP */
15490 Tcl_SetResult(interp, err, TCL_STATIC);
15491 return(TCL_ERROR);
15495 #ifdef ENABLE_LDAP
15497 peLdapQueryResults(Tcl_Interp *interp)
15499 WPLDAPRES_S *tsl;
15500 Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
15501 LDAPMessage *e;
15502 LDAP_SERV_RES_S *trl;
15503 /* returned list will be of the form:
15506 * {search-string
15507 * {name, {title, ...}, {unit, ...},
15508 * {org, ...}, {email, ...}},
15509 * ...
15510 * },
15511 * ...
15515 for(tsl = wpldap_global->ldap_search_list;
15516 tsl; tsl = tsl->next){
15517 secObj = Tcl_NewListObj(0, NULL);
15518 if(Tcl_ListObjAppendElement(interp, secObj,
15519 Tcl_NewStringObj(tsl->str ? tsl->str
15520 : "", -1)) != TCL_OK)
15521 return(TCL_ERROR);
15522 resObj = Tcl_NewListObj(0, NULL);
15523 for(trl = tsl->reslist; trl; trl = trl->next){
15524 for(e = ldap_first_entry(trl->ld, trl->res);
15525 e != NULL;
15526 e = ldap_next_entry(trl->ld, e)){
15527 char *dn;
15528 struct berval **cn, **org, **unit, **title, **mail, **sn;
15530 dn = NULL;
15531 cn = org = title = unit = mail = sn = NULL;
15533 itemObj = Tcl_NewListObj(0, NULL);
15534 peLdapEntryParse(trl, e, &cn, &org, &unit, &title,
15535 &mail, &sn);
15536 if(cn){
15537 if(Tcl_ListObjAppendElement(interp, itemObj,
15538 Tcl_NewStringObj(cn[0]->bv_val, -1)) != TCL_OK)
15539 return(TCL_ERROR);
15540 ldap_value_free_len(cn);
15542 else if(sn){
15543 if(Tcl_ListObjAppendElement(interp, itemObj,
15544 Tcl_NewStringObj(sn[0]->bv_val, -1)) != TCL_OK)
15545 return(TCL_ERROR);
15546 ldap_value_free_len(sn);
15548 else{
15549 dn = ldap_get_dn(trl->ld, e);
15551 if(dn && !dn[0]){
15552 our_ldap_dn_memfree(dn);
15553 dn = NULL;
15556 if(Tcl_ListObjAppendElement(interp, itemObj,
15557 Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK)
15558 return(TCL_ERROR);
15560 if(dn)
15561 our_ldap_dn_memfree(dn);
15563 if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR)
15564 return(TCL_ERROR);
15565 if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR)
15566 return(TCL_ERROR);
15567 if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR)
15568 return(TCL_ERROR);
15569 if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR)
15570 return(TCL_ERROR);
15571 if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK)
15572 return(TCL_ERROR);
15573 if(title)
15574 ldap_value_free_len(title);
15575 if(unit)
15576 ldap_value_free_len(unit);
15577 if(org)
15578 ldap_value_free_len(org);
15579 if(mail)
15580 ldap_value_free_len(mail);
15583 if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15584 return(TCL_ERROR);
15585 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15586 secObj) != TCL_OK)
15587 return(TCL_ERROR);
15589 return(TCL_OK);
15593 peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, struct berval **strl)
15595 Tcl_Obj *strlObj;
15596 int i;
15598 strlObj = Tcl_NewListObj(0, NULL);
15599 if(strl){
15600 for(i = 0; ALPINE_LDAP_usable(strl, i); i++){
15601 if(Tcl_ListObjAppendElement(interp, strlObj,
15602 Tcl_NewStringObj(strl[i]->bv_val, -1)) != TCL_OK)
15603 return(TCL_ERROR);
15606 if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK)
15607 return(TCL_ERROR);
15608 return(TCL_OK);
15613 init_ldap_pname(struct pine *ps)
15615 if(!ps_global->VAR_PERSONAL_NAME
15616 || ps_global->VAR_PERSONAL_NAME[0] == '\0'){
15617 char *pname;
15618 struct variable *vtmp;
15620 if(ps->maildomain && *ps->maildomain
15621 && ps->VAR_USER_ID && *ps->VAR_USER_ID){
15622 pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain);
15623 if(pname){
15624 vtmp = &ps->vars[V_PERSONAL_NAME];
15625 if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0')
15626 || (vtmp->is_fixed && !vtmp->fixed_val.p)){
15627 if(vtmp->fixed_val.p)
15628 fs_give((void **)&vtmp->fixed_val.p);
15629 vtmp->fixed_val.p = cpystr(pname);
15631 else {
15632 if(vtmp->global_val.p)
15633 fs_give((void **)&vtmp->global_val.p);
15634 vtmp->global_val.p = cpystr(pname);
15636 fs_give((void **)&pname);
15637 set_current_val(vtmp, FALSE, FALSE);
15641 return 0;
15643 #endif /* ENABLE_LDAP */
15646 * Note: this is taken straight out of pico/composer.c
15648 * strqchr - returns pointer to first non-quote-enclosed occurance of ch in
15649 * the given string. otherwise NULL.
15650 * s -- the string
15651 * ch -- the character we're looking for
15652 * q -- q tells us if we start out inside quotes on entry and is set
15653 * correctly on exit.
15654 * m -- max characters we'll check for ch (set to -1 for no check)
15656 char *
15657 strqchr(char *s, int ch, int *q, int m)
15659 int quoted = (q) ? *q : 0;
15661 for(; s && *s && m != 0; s++, m--){
15662 if(*s == '"'){
15663 quoted = !quoted;
15664 if(q)
15665 *q = quoted;
15668 if(!quoted && *s == ch)
15669 return(s);
15672 return(NULL);
15676 Tcl_Obj *
15677 wp_prune_folders(CONTEXT_S *ctxt,
15678 char *fcc,
15679 int cur_month,
15680 char *type,
15681 unsigned pr,
15682 int *ok,
15683 int moved_fldrs,
15684 Tcl_Interp *interp)
15686 Tcl_Obj *resObj = NULL, *secObj = NULL;
15687 char path2[MAXPATH+1], tmp[21];
15688 int exists, month_to_use;
15689 struct sm_folder *mail_list, *sm;
15691 mail_list = get_mail_list(ctxt, fcc);
15693 for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
15694 if(sm->month_num == cur_month - 1)
15695 break; /* matched a month */
15697 month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
15699 if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){
15700 strncpy(path2, fcc, sizeof(path2)-1);
15701 path2[sizeof(path2)-1] = '\0';
15702 strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1);
15703 tmp[sizeof(tmp)-1] = '\0';
15704 lcase((unsigned char *) tmp);
15705 snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
15707 if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){
15708 (*ok) = 0;
15709 return(NULL);
15711 else if(exists & FEX_ISFILE){
15712 if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){
15713 prune_move_folder(fcc, path2, ctxt);
15714 } else {
15715 resObj = Tcl_NewListObj(0, NULL);
15716 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15717 secObj = Tcl_NewListObj(0, NULL);
15718 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1));
15719 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1));
15720 Tcl_ListObjAppendElement(interp, resObj, secObj);
15724 if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
15725 || pr == PRUNE_NO_AND_ASK){
15726 sm = mail_list;
15727 if(!resObj && sm && sm->name && sm->name[0] != '\0'){
15728 resObj = Tcl_NewListObj(0, NULL);
15729 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15730 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15732 if(resObj)
15733 secObj = Tcl_NewListObj(0, NULL);
15734 for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){
15735 if(sm->name[0] == '\0') /* can't happen */
15736 continue;
15737 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1));
15739 if(resObj)
15740 Tcl_ListObjAppendElement(interp, resObj, secObj);
15741 } else if(resObj)
15742 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15744 free_folder_list(ctxt);
15746 if((sm = mail_list) != NULL){
15747 while(sm->name){
15748 fs_give((void **)&(sm->name));
15749 sm++;
15752 fs_give((void **)&mail_list);
15755 return(resObj);
15760 hex_colorstr(char *hexcolor, char *str)
15762 char *tstr, *p, *p2, tbuf[256];
15763 int i;
15765 strcpy(hexcolor, "000000");
15766 tstr = color_to_asciirgb(str);
15767 p = tstr;
15768 p2 = strindex(p, ',');
15769 if(p2 == NULL) return 0;
15770 strncpy(tbuf, p, min(50, p2-p));
15771 i = atoi(tbuf);
15772 sprintf(hexcolor, "%2.2x", i);
15773 p = p2+1;
15774 p2 = strindex(p, ',');
15775 if(p2 == NULL) return 0;
15776 strncpy(tbuf, p, min(50, p2-p));
15777 i = atoi(tbuf);
15778 sprintf(hexcolor+2, "%2.2x", i);
15779 p = p2+1;
15780 strncpy(tbuf, p, 50);
15781 i = atoi(tbuf);
15782 sprintf(hexcolor+4, "%2.2x", i);
15784 return 0;
15788 hexval(char ch)
15790 if(ch >= '0' && ch <= '9')
15791 return (ch - '0');
15792 else if (ch >= 'A' && ch <= 'F')
15793 return (10 + (ch - 'A'));
15794 else if (ch >= 'a' && ch <= 'f')
15795 return (10 + (ch - 'a'));
15796 return -1;
15800 ascii_colorstr(char *acolor, char *hexcolor)
15802 int i, hv;
15804 if(strlen(hexcolor) > 6) return 1;
15805 /* red value */
15806 if((hv = hexval(hexcolor[0])) == -1) return 1;
15807 i = 16 * hv;
15808 if((hv = hexval(hexcolor[1])) == -1) return 1;
15809 i += hv;
15810 sprintf(acolor, "%3.3d,", i);
15811 /* green value */
15812 if((hv = hexval(hexcolor[2])) == -1) return 1;
15813 i = 16 * hv;
15814 if((hv = hexval(hexcolor[3])) == -1) return 1;
15815 i += hv;
15816 sprintf(acolor+4, "%3.3d,", i);
15817 /* blue value */
15818 if((hv = hexval(hexcolor[4])) == -1) return 1;
15819 i = 16 * hv;
15820 if((hv = hexval(hexcolor[5])) == -1) return 1;
15821 i += hv;
15822 sprintf(acolor+8, "%3.3d", i);
15824 return 0;
15828 char *
15829 peRandomString(char *b, int l, int f)
15831 static char *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
15832 char *s = b;
15833 int j;
15834 long n;
15836 while(1){
15837 n = random();
15838 for(j = 0; j < ((sizeof(long) * 8) / 5); j++){
15839 if(l-- <= 0){
15840 *s = '\0';
15841 return(b);
15844 switch(f){
15845 case PRS_LOWER_CASE :
15846 *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15847 break;
15849 case PRS_MIXED_CASE :
15850 if(random() % 2){
15851 *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15852 break;
15855 default :
15856 *s++ = kb[(n & 0x1F)];
15857 break;
15860 n = n >> 5;
15866 long
15867 peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message)
15869 char *t,*t1,tmp[MAILTMPLEN];
15870 unsigned long u;
15871 MESSAGECACHE *elt;
15872 APPEND_PKG *ap = (APPEND_PKG *) data;
15873 *flags = *date = NIL; /* assume no flags or date */
15874 if (ap->flags) fs_give ((void **) &ap->flags);
15875 if (ap->date) fs_give ((void **) &ap->date);
15876 mail_gc (ap->stream,GC_TEXTS);
15877 if (++ap->msgno <= ap->msgmax) {
15878 /* initialize flag string */
15879 memset (t = tmp,0,MAILTMPLEN);
15880 /* output system flags */
15881 if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15882 if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15883 if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15884 if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15885 if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15886 if ((u = elt->user_flags) != 0L) do /* any user flags? */
15887 if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long)
15888 (2 + strlen
15889 (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) {
15890 if(t-tmp < sizeof(tmp))
15891 *t++ = ' '; /* space delimiter */
15892 strncpy (t,t1,sizeof(tmp)-(t-tmp)); /* copy the user flag */
15894 while (u); /* until no more user flags */
15895 tmp[sizeof(tmp)-1] = '\0';
15896 *flags = ap->flags = cpystr (tmp + 1);
15897 *date = ap->date = cpystr (mail_date (tmp,elt));
15898 *message = ap->message; /* message stringstruct */
15899 INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
15901 else *message = NIL; /* all done */
15902 return LONGT;
15906 /* Initialize file string structure for file stringstruct
15907 * Accepts: string structure
15908 * pointer to message data structure
15909 * size of string
15912 void
15913 ms_init(STRING *s, void *data, unsigned long size)
15915 APPEND_PKG *md = (APPEND_PKG *) data;
15916 s->data = data; /* note stream/msgno and header length */
15917 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT);
15918 mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL);
15919 s->size += s->data1; /* header + body size */
15920 SETPOS (s,0);
15924 /* Get next character from file stringstruct
15925 * Accepts: string structure
15926 * Returns: character, string structure chunk refreshed
15928 char
15929 ms_next(STRING *s)
15931 char c = *s->curpos++; /* get next byte */
15932 SETPOS (s,GETPOS (s)); /* move to next chunk */
15933 return c; /* return the byte */
15937 /* Set string pointer position for file stringstruct
15938 * Accepts: string structure
15939 * new position
15941 void
15942 ms_setpos(STRING *s, unsigned long i)
15944 APPEND_PKG *md = (APPEND_PKG *) s->data;
15945 if (i < s->data1) { /* want header? */
15946 s->chunk = mail_fetchheader (md->stream,md->msgno);
15947 s->chunksize = s->data1; /* header length */
15948 s->offset = 0; /* offset is start of message */
15950 else if (i < s->size) { /* want body */
15951 s->chunk = mail_fetchtext (md->stream,md->msgno);
15952 s->chunksize = s->size - s->data1;
15953 s->offset = s->data1; /* offset is end of header */
15955 else { /* off end of message */
15956 s->chunk = NIL; /* make sure that we crack on this then */
15957 s->chunksize = 1; /* make sure SNX cracks the right way... */
15958 s->offset = i;
15960 /* initial position and size */
15961 s->curpos = s->chunk + (i -= s->offset);
15962 s->cursize = s->chunksize - i;
15967 remote_pinerc_failure(void)
15969 snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s",
15970 ps_global->c_client_error[0]
15971 ? ps_global->c_client_error
15972 : _("Unable to read remote configuration"));
15974 return(TRUE);
15977 char *
15978 peWebAlpinePrefix(void)
15980 return("Web ");
15984 void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){
15985 char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
15986 *folder = NULL, intro[MAILTMPLEN+1];
15987 long number;
15988 ENVELOPE *e = NULL;
15989 Tcl_Obj *resObj;
15991 if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){
15993 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream)));
15994 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n)));
15996 if(stream){
15997 e = pine_mail_fetchstructure(stream, n, NULL);
15999 if(sp_flagged(stream, SP_INBOX))
16000 folder = NULL;
16001 else{
16002 folder = STREAMNAME(stream);
16003 if(folder[0] == '?' && folder[1] == '\0')
16004 folder = NULL;
16008 format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro));
16010 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
16011 "%s%s%s%.80s%.80s", intro,
16012 from ? ((number > 1L) ? " Most recent f" : " F") : "",
16013 from ? "rom " : "",
16014 from ? from : "",
16015 subjtext);
16017 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(tmp_20k_buf,-1));
16019 Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj);
16024 /* * * * * * * * * RSS 2.0 Support Routines * * * * * * * * * * * */
16027 * PERssCmd - RSS TCL interface
16030 PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
16032 char *s1;
16034 dprint((2, "PERssCmd"));
16036 if(objc == 1){
16037 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
16038 return(TCL_ERROR);
16040 s1 = Tcl_GetStringFromObj(objv[1], NULL);
16042 if(s1){
16043 if(!strcmp(s1, "news")){
16044 return(peRssReturnFeed(interp, "news", ps_global->VAR_RSS_NEWS));
16046 else if(!strcmp(s1, "weather")){
16047 return(peRssReturnFeed(interp, "weather", ps_global->VAR_RSS_WEATHER));
16051 Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC);
16052 return(TCL_ERROR);
16056 * peRssReturnFeed - fetch feed contents and package Tcl response
16059 peRssReturnFeed(Tcl_Interp *interp, char *type, char *link)
16061 RSS_FEED_S *feed;
16062 char *errstr = "UNKNOWN";
16064 if(link){
16065 ps_global->c_client_error[0] = '\0';
16067 if((feed = peRssFeed(interp, type, link)) != NULL)
16068 return(peRssPackageFeed(interp, feed));
16070 if(ps_global->mm_log_error)
16071 errstr = ps_global->c_client_error;
16073 else
16074 errstr = "missing setting";
16076 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr);
16077 Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
16078 return(TCL_ERROR);
16082 * peRssPackageFeed - build a list of feed item elements
16084 * LIST ORDER: {title} {link} {description} {image}
16087 peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed)
16089 RSS_ITEM_S *item;
16091 for(item = feed->items; item; item = item->next)
16092 if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s",
16093 (item->title && *item->title)? item->title : "Feed Provided No Title",
16094 item->link ? item->link : "",
16095 item->description ? item->description : "",
16096 feed->image ? feed->image : "") != TCL_OK)
16097 return(TCL_ERROR);
16099 return(TCL_OK);
16104 * peRssFeed - return cached feed struct or fetch a new one
16106 RSS_FEED_S *
16107 peRssFeed(Tcl_Interp *interp, char *type, char *link)
16109 int i, cache_l, cp_ref;
16110 time_t now = time(0);
16111 RSS_FEED_S *feed = NULL;
16112 RSS_CACHE_S *cache, *cp;
16113 static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE];
16115 if(!strucmp(type,"news")){
16116 cache = &news_cache[0];
16117 cache_l = RSS_NEWS_CACHE_SIZE;
16119 else{
16120 cache = &weather_cache[0];
16121 cache_l = RSS_WEATHER_CACHE_SIZE;
16124 /* search/purge cache */
16125 for(i = 0; i < cache_l; i++)
16126 if(cache[i].link){
16127 if(now > cache[i].stale){
16128 peRssClearCacheEntry(&cache[i]);
16130 else if(!strcmp(link, cache[i].link)){
16131 cache[i].referenced++;
16132 return(cache[i].feed); /* HIT! */
16136 if((feed = peRssFetch(interp, link)) != NULL){
16137 /* find cache slot, and insert feed into cache */
16138 for(i = 0, cp_ref = 0; i < cache_l; i++)
16139 if(!cache[i].feed){
16140 cp = &cache[i];
16141 break;
16143 else if(cache[i].referenced >= cp_ref)
16144 cp = &cache[i];
16146 if(!cp)
16147 cp = &cache[0]; /* failsafe */
16149 peRssClearCacheEntry(cp); /* make sure */
16151 cp->link = cpystr(link);
16152 cp->feed = feed;
16153 cp->referenced = 0;
16154 cp->stale = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60);
16157 return(feed);
16161 * peRssFetch - follow the provided link an return the resulting struct
16163 RSS_FEED_S *
16164 peRssFetch(Tcl_Interp *interp, char *link)
16166 char *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL;
16167 char *buffer = NULL, *bp, *p, *q;
16168 int ttl = 60;
16169 unsigned long port = 0L, buffer_len = 0L;
16170 time_t theirdate = 0;
16171 STORE_S *feed_so = NULL;
16172 TCPSTREAM *tcp_stream;
16174 if(link){
16175 /* grok url */
16176 rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag);
16177 if(scheme && loc && path){
16178 if((p = strchr(loc,':')) != NULL){
16179 *p++ = '\0';
16180 while(*p && isdigit((unsigned char) *p))
16181 port = ((port * 10) + (*p++ - '0'));
16183 if(*p){
16184 Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC);
16185 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16186 return(NULL);
16190 if(scheme && !strucmp(scheme, "feed")){
16191 fs_give((void **) &scheme);
16192 scheme = cpystr("http");
16195 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5);
16196 tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT);
16197 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
16199 if(tcp_stream != NULL){
16200 char rev[128];
16202 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "GET /%s%s%s%s%s HTTP/1.1\r\nHost: %s\r\nAccept: application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nUser-Agent: Web-Alpine/%s (%s %s)\r\n\r\n",
16203 path, parms ? ":" : "", parms ? parms : "",
16204 query ? "?" : "", query ? query : "", loc,
16205 ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev)));
16207 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5);
16208 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5);
16210 if(tcp_sout(tcp_stream, tmp_20k_buf, strlen(tmp_20k_buf))){
16211 int ok = 0, chunked = FALSE;
16213 while((p = tcp_getline(tcp_stream)) != NULL){
16214 if(!ok){
16215 ok++;
16216 if(strucmp(p,"HTTP/1.1 200 OK")){
16217 fs_give((void **) &p);
16218 break; /* bail */
16221 else if(*p == '\0'){ /* first blank line, start of body */
16222 if(buffer || feed_so){
16223 fs_give((void **) &p);
16224 break; /* bail */
16227 if(buffer_len){
16228 buffer = fs_get(buffer_len + 16);
16229 if(!tcp_getbuffer(tcp_stream, buffer_len, buffer))
16230 fs_give((void **) &buffer);
16232 fs_give((void **) &p);
16233 break; /* bail */
16235 else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
16236 fs_give((void **) &p);
16237 break; /* bail */
16240 else if(feed_so){ /* collect body */
16241 if(chunked){
16242 int chunk_len = 0, gotbuf;
16244 /* first line is chunk size in hex */
16245 for(q = p; *q && isxdigit((unsigned char) *q); q++)
16246 chunk_len = (chunk_len * 16) + XDIGIT2C(*q);
16248 if(chunk_len > 0){ /* collect chunk */
16249 char *tbuf = fs_get(chunk_len + 16);
16250 gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf);
16251 if(gotbuf)
16252 so_nputs(feed_so, tbuf, chunk_len);
16254 fs_give((void **) &tbuf);
16256 if(!gotbuf){
16257 fs_give((void **) &p);
16258 break; /* bail */
16262 /* collect trailing CRLF */
16263 gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0');
16264 if(q)
16265 fs_give((void **) &q);
16267 if(chunk_len == 0 || !gotbuf){
16268 fs_give((void **) &p);
16269 break; /* bail */
16272 else
16273 so_puts(feed_so, p);
16275 else{ /* in header, grok fields */
16276 if((q = strchr(p,':')) != NULL){
16277 int l = q - p;
16279 *q++ = '\0';
16280 while(isspace((unsigned char ) *q))
16281 q++;
16283 /* content-length */
16284 if(l == 4 && !strucmp(p, "date")){
16285 theirdate = date_to_local_time_t(q);
16287 else if(l == 7 && !strucmp(p, "expires")){
16288 time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0));
16290 if(expires > 0 && expires < (8 * 60 * 60))
16291 ttl = expires;
16293 else if(l == 12 && !strucmp(p, "content-type")
16294 && struncmp(q,"text/xml", 8)
16295 && struncmp(q,"application/xhtml+xml", 21)
16296 && struncmp(q,"application/rss+xml", 19)
16297 && struncmp(q,"application/xml", 15)){
16298 fs_give((void **) &p);
16299 break; /* bail */
16301 else if(l == 13 && !strucmp(p, "cache-control")){
16302 if(!struncmp(q,"max-age=",8)){
16303 int secs = 0;
16305 for(q += 8; *q && isdigit((unsigned char) *q); q++)
16306 secs = ((secs * 10) + (*q - '0'));
16308 if(secs > 0)
16309 ttl = secs;
16312 else if(l == 14 && !strucmp(p,"content-length")){
16313 while(*q && isdigit((unsigned char) *q))
16314 buffer_len = ((buffer_len * 10) + (*q++ - '0'));
16316 if(*q){
16317 fs_give((void **) &p);
16318 break; /* bail */
16321 else if(l == 17 && !strucmp(p, "transfer-encoding")){
16322 if(!struncmp(q,"chunked", 7)){
16323 chunked = TRUE;
16325 else{ /* unknown encoding */
16326 fs_give((void **) &p);
16327 break; /* bail */
16333 fs_give((void **) &p);
16336 else{
16337 Tcl_SetResult(interp, "RSS send failure", TCL_STATIC);
16338 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16341 tcp_close(tcp_stream);
16342 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
16343 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60);
16344 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16346 if(feed_so){
16347 buffer = (char *) so_text(feed_so);
16348 buffer_len = (int) so_tell(feed_so);
16351 if(buffer && buffer_len){
16352 RSS_FEED_S *feed;
16353 char *err;
16354 STORE_S *bucket;
16355 gf_io_t gc, pc;
16357 /* grok response */
16358 bucket = so_get(CharStar, NULL, EDIT_ACCESS);
16359 gf_set_readc(&gc, buffer, buffer_len, CharStar, 0);
16360 gf_set_so_writec(&pc, bucket);
16361 gf_filter_init();
16362 gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0));
16363 if((err = gf_pipe(gc, pc)) != NULL){
16364 gf_html2plain_rss_free(&feed);
16365 Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16368 so_give(&bucket);
16370 if(feed_so)
16371 so_give(&feed_so);
16372 else
16373 fs_give((void **) &buffer);
16375 return(feed);
16377 else
16378 Tcl_SetResult(interp, "RSS response error", TCL_STATIC);
16380 else
16381 Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16383 else
16384 Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC);
16386 else
16387 Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC);
16389 return(NULL);
16393 void
16394 peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag)
16396 if(scheme) fs_give((void **) scheme);
16397 if(loc) fs_give((void **) loc);
16398 if(path) fs_give((void **) path);
16399 if(parms) fs_give((void **) parms);
16400 if(query) fs_give((void **) query);
16401 if(frag) fs_give((void **) frag);
16404 void
16405 peRssClearCacheEntry(RSS_CACHE_S *entry)
16407 if(entry){
16408 if(entry->link)
16409 fs_give((void **) &entry->link);
16411 gf_html2plain_rss_free(&entry->feed);
16412 memset(entry, 0, sizeof(RSS_CACHE_S));