* Implement a different way to delete a password from the cache.
[alpine.git] / web / src / alpined.d / alpined.c
blobb5d8420ab019c76f5eabc1a9d2858bf277d5d1c4
1 /* ========================================================================
2 * Copyright 2006-2008 University of Washington
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * ========================================================================
13 /* ========================================================================
14 Implement alpine TCL interfaces. Execute TCL interfaces
15 via interpreter reading commands and writing results over
16 UNIX domain socket.
17 ======================================================================== */
20 #include <system.h>
21 #include <general.h>
23 #include "../../../c-client/c-client.h"
24 #include "../../../c-client/imap4r1.h"
26 #include "../../../pith/osdep/color.h" /* color support library */
27 #include "../../../pith/osdep/canaccess.h"
28 #include "../../../pith/osdep/temp_nam.h"
29 #include "../../../pith/osdep/collate.h"
30 #include "../../../pith/osdep/filesize.h"
31 #include "../../../pith/osdep/writ_dir.h"
32 #include "../../../pith/osdep/err_desc.h"
34 #include "../../../pith/stream.h"
35 #include "../../../pith/context.h"
36 #include "../../../pith/state.h"
37 #include "../../../pith/msgno.h"
38 #include "../../../pith/debug.h"
39 #include "../../../pith/init.h"
40 #include "../../../pith/conf.h"
41 #include "../../../pith/conftype.h"
42 #include "../../../pith/detoken.h"
43 #include "../../../pith/flag.h"
44 #include "../../../pith/help.h"
45 #include "../../../pith/remote.h"
46 #include "../../../pith/status.h"
47 #include "../../../pith/mailcmd.h"
48 #include "../../../pith/savetype.h"
49 #include "../../../pith/save.h"
50 #include "../../../pith/reply.h"
51 #include "../../../pith/sort.h"
52 #include "../../../pith/ldap.h"
53 #include "../../../pith/addrbook.h"
54 #include "../../../pith/ablookup.h"
55 #include "../../../pith/takeaddr.h"
56 #include "../../../pith/bldaddr.h"
57 #include "../../../pith/copyaddr.h"
58 #include "../../../pith/thread.h"
59 #include "../../../pith/folder.h"
60 #include "../../../pith/mailview.h"
61 #include "../../../pith/indxtype.h"
62 #include "../../../pith/icache.h"
63 #include "../../../pith/mailindx.h"
64 #include "../../../pith/mailpart.h"
65 #include "../../../pith/mimedesc.h"
66 #include "../../../pith/detach.h"
67 #include "../../../pith/newmail.h"
68 #include "../../../pith/charset.h"
69 #include "../../../pith/util.h"
70 #include "../../../pith/rfc2231.h"
71 #include "../../../pith/string.h"
72 #include "../../../pith/send.h"
73 #include "../../../pith/options.h"
74 #include "../../../pith/list.h"
75 #include "../../../pith/mimetype.h"
76 #include "../../../pith/mailcap.h"
77 #include "../../../pith/sequence.h"
78 #include "../../../pith/smime.h"
79 #include "../../../pith/url.h"
80 #include "../../../pith/charconv/utf8.h"
82 #include "alpined.h"
83 #include "color.h"
84 #include "imap.h"
85 #include "ldap.h"
86 #include "debug.h"
87 #include "stubs.h"
89 #include <tcl.h>
93 * Fake screen dimension for word wrap and such
95 #define FAKE_SCREEN_WIDTH 80
96 #define FAKE_SCREEN_LENGTH 24
99 * Arbitrary minimum display width (in characters)
101 #define MIN_SCREEN_COLS 20
105 * Maximum number of lines allowed in signatures
107 #define SIG_MAX_LINES 24
108 #define SIG_MAX_COLS 1024
112 * Number of seconds we'll wait before we assume the client has wondered
113 * on to more interesting content
115 #define PE_INPUT_TIMEOUT 1800
119 * Posting error length max
121 #define WP_MAX_POST_ERROR 128
125 * AUTH Response Tokens
127 #define AUTH_EMPTY_STRING "NOPASSWD"
128 #define AUTH_FAILURE_STRING "BADPASSWD"
131 * CERT Response Tokens
133 #define CERT_QUERY_STRING "CERTQUERY"
134 #define CERT_FAILURE_STRING "CERTFAIL"
138 * Charset used within alpined and to communicate with alpined
139 * Note: posting-charset still respected
141 #define WP_INTERNAL_CHARSET "UTF-8"
145 * Globals referenced throughout pine...
147 struct pine *wps_global; /* THE global variable! */
151 * More global state
153 long gPeITop, gPeICount;
155 long gPeInputTimeout = PE_INPUT_TIMEOUT;
156 long gPEAbandonTimeout = 0;
160 * Authorization issues
162 int peNoPassword, peCredentialError;
163 int peCertFailure, peCertQuery;
164 char peCredentialRequestor[CRED_REQ_SIZE];
166 char *peSocketName;
168 char **peTSig;
170 CONTEXT_S *config_context_list;
172 STRLIST_S *peCertHosts;
174 bitmap_t changed_feature_list;
175 #define F_CH_ON(feature) (bitnset((feature),changed_feature_list))
176 #define F_CH_OFF(feature) (!F_CH_ON(feature))
177 #define F_CH_TURN_ON(feature) (setbitn((feature),changed_feature_list))
178 #define F_CH_TURN_OFF(feature) (clrbitn((feature),changed_feature_list))
179 #define F_CH_SET(feature,value) ((value) ? F_CH_TURN_ON((feature)) \
180 : F_CH_TURN_OFF((feature)))
183 typedef struct _status_msg {
184 time_t posted;
185 unsigned type:3;
186 unsigned seen:1;
187 long id;
188 char *text;
189 struct _status_msg *next;
190 } STATMSG_S;
192 static STATMSG_S *peStatList;
194 typedef struct _composer_attachment {
195 unsigned file:1;
196 unsigned body:1;
197 char *id;
198 union {
199 struct {
200 char *local;
201 char *remote;
202 char *type;
203 char *subtype;
204 char *description;
205 long size;
206 } f;
207 struct {
208 BODY *body;
209 } b;
210 struct {
211 long msgno;
212 char *part;
213 } msg;
214 } l;
215 struct _composer_attachment *next;
216 } COMPATT_S;
218 static COMPATT_S *peCompAttach;
221 * Holds data passed
223 typedef struct _msg_data {
224 ENVELOPE *outgoing;
225 METAENV *metaenv;
226 PINEFIELD *custom;
227 STORE_S *msgtext;
228 STRLIST_S *attach;
229 char *fcc;
230 int fcc_colid;
231 int postop_fcc_no_attach;
232 char *charset;
233 char *priority;
234 int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *);
235 unsigned flowed:1;
236 unsigned html:1;
237 unsigned qualified_addrs:1;
238 } MSG_COL_S;
242 * locally global structure to keep track of various bits of state
243 * needed to collect filtered output
245 static struct _embedded_data {
246 Tcl_Interp *interp;
247 Tcl_Obj *obj;
248 STORE_S *store;
249 long uid;
250 HANDLE_S *handles;
251 char inhandle;
252 ENVELOPE *env;
253 BODY *body;
254 struct {
255 char fg[7];
256 char bg[7];
257 char fgdef[7];
258 char bgdef[7];
259 } color;
260 } peED;
264 * RSS stream cache
266 typedef struct _rss_cache_s {
267 char *link;
268 time_t stale;
269 int referenced;
270 RSS_FEED_S *feed;
271 } RSS_CACHE_S;
273 #define RSS_NEWS_CACHE_SIZE 1
274 #define RSS_WEATHER_CACHE_SIZE 1
277 #ifdef ENABLE_LDAP
278 WPLDAP_S *wpldap_global;
279 #endif
282 * random string generator flags
284 #define PRS_NONE 0x0000
285 #define PRS_LOWER_CASE 0x0001
286 #define PRS_UPPER_CASE 0x0002
287 #define PRS_MIXED_CASE 0x0004
290 * peSaveWork flag definitions
292 #define PSW_NONE 0x00
293 #define PSW_COPY 0x01
294 #define PSW_MOVE 0x02
297 * Message Collector flags
299 #define PMC_NONE 0x00
300 #define PMC_FORCE_QUAL 0x01
301 #define PMC_PRSRV_ATT 0x02
304 * length of thread info string
306 #define WP_MAX_THRD_S 64
309 * static buf size for putenv() if necessary
311 #define PUTENV_MAX 64
315 /*----------------------------------------------------------------------
316 General use big buffer. It is used in the following places:
317 compose_mail: while parsing header of postponed message
318 append_message2: while writing header into folder
319 q_status_messageX: while doing printf formatting
320 addr_book: Used to return expanded address in. (Can only use here
321 because mm_log doesn't q_status on PARSE errors !)
322 alpine.c: When address specified on command line
323 init.c: When expanding variable values
324 and many many more...
326 ----*/
327 char wtmp_20k_buf[20480];
332 /* Internal prototypes */
333 void peReturn(int, char *, const char *);
334 int peWrite(int, char *);
335 char *peCreateUserContext(Tcl_Interp *, char *, char *, char *);
336 void peDestroyUserContext(struct pine **);
337 char *peLoadConfig(struct pine *);
338 int peCreateStream(Tcl_Interp *, CONTEXT_S *, char *, int);
339 void peDestroyStream(struct pine *);
340 void pePrepareForAuthException(void);
341 char *peAuthException(void);
342 void peInitVars(struct pine *);
343 int peSelect(Tcl_Interp *, int, Tcl_Obj **, int);
344 int peSelectNumber(Tcl_Interp *, int, Tcl_Obj **, int);
345 int peSelectDate(Tcl_Interp *, int, Tcl_Obj **, int);
346 int peSelectText(Tcl_Interp *, int, Tcl_Obj **, int);
347 int peSelectStatus(Tcl_Interp *, int, Tcl_Obj **, int);
348 char *peSelValTense(Tcl_Obj *);
349 char *peSelValYear(Tcl_Obj *);
350 char *peSelValMonth(Tcl_Obj *);
351 char *peSelValDay(Tcl_Obj *);
352 int peSelValCase(Tcl_Obj *);
353 int peSelValField(Tcl_Obj *);
354 int peSelValFlag(Tcl_Obj *);
355 int peSelected(Tcl_Interp *, int, Tcl_Obj **, int);
356 int peSelectError(Tcl_Interp *, char *);
357 int peApply(Tcl_Interp *, int, Tcl_Obj **);
358 char *peApplyFlag(MAILSTREAM *, MSGNO_S *, char, int, long *);
359 int peApplyError(Tcl_Interp *, char *);
360 int peIndexFormat(Tcl_Interp *);
361 int peAppendIndexParts(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
362 int peAppendIndexColor(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
363 int peMessageStatusBits(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
364 char *peMsgStatBitString(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
365 Tcl_Obj *peMsgStatNameList(Tcl_Interp *, struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
366 int peNewMailResult(Tcl_Interp *);
367 int peMessageSize(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
368 int peMessageDate(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
369 int peMessageSubject(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
370 int peMessageFromAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
371 int peMessageToAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
372 int peMessageCcAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
373 int peMessageField(Tcl_Interp *, imapuid_t, char *);
374 int peMessageStatus(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
375 int peMessageCharset(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
376 int peMessageBounce(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
377 int peMessageSpamNotice(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
378 char *peSendSpamReport(long, char *, char *, char *);
379 int peMsgnoFromUID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
380 int peMessageText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
381 int peMessageHeader(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
382 void peFormatEnvelope(MAILSTREAM *, long, char *, ENVELOPE *, gf_o_t, long, char *, int);
383 void peFormatEnvelopeAddress(MAILSTREAM *, long, char *, char *, ADDRESS *, int, char *, gf_o_t);
384 void peFormatEnvelopeNewsgroups(char *, char *, int, gf_o_t);
385 void peFormatEnvelopeText(char *, char *);
386 int peMessageAttachments(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
387 int peMessageBody(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
388 int peMessagePartFromCID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
389 int peLocateBodyByCID(char *, char *, BODY *);
390 char *peColorStr(char *, char *);
391 int peInterpWritec(int);
392 int peInterpFlush(void);
393 int peNullWritec(int);
394 void peGetMimeTyping(BODY *, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **);
395 int peGetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
396 int peIsFlagged(MAILSTREAM *, imapuid_t, char *);
397 int peSetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
398 int peMsgSelect(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
399 int peReplyHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
400 int peAppListF(Tcl_Interp *, Tcl_Obj *, char *, ...);
401 void pePatAppendID(Tcl_Interp *, Tcl_Obj *, PAT_S *);
402 void pePatAppendPattern(Tcl_Interp *, Tcl_Obj *, PAT_S *);
403 char *pePatStatStr(int);
404 int peReplyText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
405 int peSoStrToList(Tcl_Interp *, Tcl_Obj *, STORE_S *);
406 int peForwardHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
407 int peForwardText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
408 int peDetach(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
409 int peAttachInfo(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
410 int peSaveDefault(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
411 int peSaveWork(Tcl_Interp *, imapuid_t, int, Tcl_Obj **, long);
412 int peSave(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
413 int peCopy(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
414 int peMove(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
415 int peGotoDefault(Tcl_Interp *, imapuid_t, Tcl_Obj **);
416 int peTakeaddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
417 int peTakeFrom(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
418 int peAddSuggestedContactInfo(Tcl_Interp *, Tcl_Obj *, ADDRESS *);
419 int peReplyQuote(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
420 long peMessageNumber(imapuid_t);
421 long peSequenceNumber(imapuid_t);
422 int peMsgCollector(Tcl_Interp *, int, Tcl_Obj **,
423 int (*)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long);
424 int peMsgCollected(Tcl_Interp *, MSG_COL_S *, char *, long);
425 void peMsgSetParm(PARAMETER **, char *, char *);
426 Tcl_Obj *peMsgAttachCollector(Tcl_Interp *, BODY *);
427 int peFccAppend(Tcl_Interp *, Tcl_Obj *, char *, int);
428 int peDoPost(METAENV *, BODY *, char *, CONTEXT_S **, char *);
429 int peDoPostpone(METAENV *, BODY *, char *, CONTEXT_S **, char *);
430 int peWriteSig (Tcl_Interp *, char *, Tcl_Obj **);
431 int peInitAddrbooks(Tcl_Interp *, int);
432 int peRuleStatVal(char *, int *);
433 int peRuleSet(Tcl_Interp *, Tcl_Obj **);
434 int peAppendCurrentSort(Tcl_Interp *interp);
435 int peAppendDefaultSort(Tcl_Interp *interp);
436 #if 0
437 ADDRESS *peAEToAddress(AdrBk_Entry *);
438 char *peAEFcc(AdrBk_Entry *);
439 #endif
440 NAMEVAL_S *sort_key_rules(int);
441 NAMEVAL_S *wp_indexheight_rules(int);
442 PINEFIELD *peCustomHdrs(void);
443 STATMSG_S *sml_newmsg(int, char *);
444 char *sml_getmsg(void);
445 char **sml_getmsgs(void);
446 void sml_seen(void);
447 #ifdef ENABLE_LDAP
448 int peLdapQueryResults(Tcl_Interp *);
449 int peLdapStrlist(Tcl_Interp *, Tcl_Obj *, struct berval **);
450 int init_ldap_pname(struct pine *);
451 #endif /* ENABLE_LDAP */
452 char *strqchr(char *, int, int *, int);
453 Tcl_Obj *wp_prune_folders(CONTEXT_S *, char *, int, char *,
454 unsigned, int *, int, Tcl_Interp *);
455 int hex_colorstr(char *, char *);
456 int hexval(char);
457 int ascii_colorstr(char *, char *);
458 COMPATT_S *peNewAttach(void);
459 void peFreeAttach(COMPATT_S **);
460 COMPATT_S *peGetAttachID(char *);
461 char *peFileAttachID(char *, char *, char *, char *, char *, int);
462 char *peBodyAttachID(BODY *);
463 void peBodyMoveContents(BODY *, BODY *);
464 int peClearAttachID(char *);
465 char *peRandomString(char *, int, int);
466 void ms_init(STRING *, void *, unsigned long);
467 char ms_next(STRING *);
468 void ms_setpos(STRING *, unsigned long);
469 long peAppendMsg(MAILSTREAM *, void *, char **, char **, STRING **);
470 int remote_pinerc_failure(void);
471 char *peWebAlpinePrefix(void);
472 void peNewMailAnnounce(MAILSTREAM *, long, long);
473 int peMessageNeedPassphrase(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
474 int peRssReturnFeed(Tcl_Interp *, char *, char *);
475 int peRssPackageFeed(Tcl_Interp *, RSS_FEED_S *);
476 RSS_FEED_S *peRssFeed(Tcl_Interp *, char *, char *);
477 RSS_FEED_S *peRssFetch(Tcl_Interp *, char *);
478 void peRssComponentFree(char **,char **,char **,char **,char **,char **);
479 void peRssClearCacheEntry(RSS_CACHE_S *);
482 /* Prototypes for Tcl-exported methods */
483 int PEInit(Tcl_Interp *interp, char *);
484 void PEExitCleanup(ClientData);
485 int PEInfoCmd(ClientData clientData, Tcl_Interp *interp,
486 int objc, Tcl_Obj *CONST objv[]);
487 int PEConfigCmd(ClientData clientData, Tcl_Interp *interp,
488 int objc, Tcl_Obj *CONST objv[]);
489 int PEDebugCmd(ClientData clientData, Tcl_Interp *interp,
490 int objc, Tcl_Obj *CONST objv[]);
491 int PESessionCmd(ClientData clientData, Tcl_Interp *interp,
492 int objc, Tcl_Obj *CONST objv[]);
493 int PEMailboxCmd(ClientData clientData, Tcl_Interp *interp,
494 int objc, Tcl_Obj *CONST objv[]);
495 int PEThreadCmd(ClientData clientData, Tcl_Interp *interp,
496 int objc, Tcl_Obj *CONST objv[]);
497 int PEMessageCmd(ClientData clientData, Tcl_Interp *interp,
498 int objc, Tcl_Obj *CONST objv[]);
499 int PEFolderCmd(ClientData clientData, Tcl_Interp *interp,
500 int objc, Tcl_Obj *CONST objv[]);
501 int PEComposeCmd(ClientData clientData, Tcl_Interp *interp,
502 int objc, Tcl_Obj *CONST objv[]);
503 int PEPostponeCmd(ClientData clientData, Tcl_Interp *interp,
504 int objc, Tcl_Obj *CONST objv[]);
505 int PEAddressCmd(ClientData clientData, Tcl_Interp *interp,
506 int objc, Tcl_Obj *CONST objv[]);
507 int PEClistCmd(ClientData clientData, Tcl_Interp *interp,
508 int objc, Tcl_Obj *CONST objv[]);
509 int PELdapCmd(ClientData clientData, Tcl_Interp *interp,
510 int objc, Tcl_Obj *CONST objv[]);
511 int PERssCmd(ClientData clientData, Tcl_Interp *interp,
512 int objc, Tcl_Obj *CONST objv[]);
514 /* Append package */
515 typedef struct append_pkg {
516 MAILSTREAM *stream; /* source stream */
517 unsigned long msgno; /* current message number */
518 unsigned long msgmax; /* maximum message number */
519 char *flags; /* current flags */
520 char *date; /* message internal date */
521 STRING *message; /* stringstruct of message */
522 } APPEND_PKG;
524 STRINGDRIVER mstring = {
525 ms_init, /* initialize string structure */
526 ms_next, /* get next byte in string structure */
527 ms_setpos /* set position in string structure */
531 /*----------------------------------------------------------------------
532 main routine -- entry point
534 Args: argv, argc -- The command line arguments
537 Setup c-client drivers and dive into TCL interpreter engine
539 ----*/
542 main(int argc, char *argv[])
544 int ev = 1, s, cs, n, co, o, l, bl = 256, argerr;
545 char *buf, sname[256];
546 struct sockaddr_un name;
547 Tcl_Interp *interp;
548 #if PUBCOOKIE
549 extern AUTHENTICATOR auth_gss_proxy;
550 #endif
552 srandom(getpid() + time(0));
554 /*----------------------------------------------------------------------
555 Initialize c-client
556 ----------------------------------------------------------------------*/
559 * NO LOCAL DRIVERS ALLOWED
560 * For this to change pintecld *MUST* be running under the user's UID and
561 * and signal.[ch] need to get fixed to handle KOD rather than change
562 * the debug level
564 mail_link (&imapdriver); /* link in the imap driver */
565 mail_link (&unixdriver); /* link in the unix driver */
566 mail_link (&dummydriver); /* link in the dummy driver */
568 /* link authentication drivers */
569 #if PUBCOOKIE
570 auth_link (&auth_gss_proxy); /* pubcoookie proxy authenticator */
571 #endif
572 auth_link (&auth_md5); /* link in the md5 authenticator */
573 auth_link (&auth_pla);
574 auth_link (&auth_log); /* link in the log authenticator */
575 ssl_onceonlyinit ();
576 mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);
578 #if PUBCOOKIE
579 /* if REMOTE_USER set, use it as username */
580 if(buf = getenv("REMOTE_USER"))
581 env_init(buf, "/tmp");
582 #endif
584 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){
585 fprintf(stderr, "Can't disable unix driver");
586 exit(1);
590 * Set network timeouts so we don't hang forever
591 * The open timeout can be pretty short since we're
592 * just opening tcp connection. The read timeout needs
593 * to be longer because the response to some actions can
594 * take awhile. Hopefully this is well within httpd's
595 * cgi timeout threshold.
597 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
598 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
600 /*----------------------------------------------------------------------
601 Initialize pith library
602 ----------------------------------------------------------------------*/
603 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
604 pith_opt_user_agent_prefix = peWebAlpinePrefix;
605 pith_opt_newmail_announce = peNewMailAnnounce;
607 setup_for_index_index_screen();
610 /*----------------------------------------------------------------------
611 Parse arguments
612 ----------------------------------------------------------------------*/
613 debug = 0;
614 for(argerr = 0; !argerr && ((n = getopt(argc,argv,"d")) != -1); ) {
615 switch(n) {
616 case 'd' : debug++; break;
617 case '?' : argerr = 1; break;
621 if(argerr || optind != argc){
622 char *p = strrchr(argv[0],'/');
623 fprintf(stderr, "Usage: %s [-d]\n", p ? p + 1 : argv[0]);
624 exit(1);
627 /*----------------------------------------------------------------------
628 Hop into the Tcl processing loop
629 ----------------------------------------------------------------------*/
631 buf = (char *) fs_get(bl * sizeof(char));
633 if(fgets(sname, 255, stdin) && *sname){
634 if(sname[l = strlen(sname) - 1] == '\n')
635 sname[l] = '\0';
637 if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){
639 name.sun_family = AF_UNIX;
640 strcpy(name.sun_path, peSocketName = sname);
641 l = sizeof(name);
643 if(bind(s, (struct sockaddr *) &name, l) == 0){
644 if(listen(s, 5) == 0){
646 * after the groundwork's done, go into the background.
647 * the fork saves the caller from invoking us in the background
648 * which introduces a timing race between the first client
649 * request arrival and our being prepared to accept it.
651 if(debug < 10){
652 switch(fork()){
653 case -1 : /* error */
654 perror("fork");
655 exit(1);
657 case 0 : /* child */
658 close(0); /* disassociate */
659 close(1);
660 close(2);
661 setpgrp(0, 0);
662 break;
664 default : /* parent */
665 exit(0);
669 debug_init();
670 dprint((SYSDBG_INFO, "started"));
672 interp = Tcl_CreateInterp();
674 PEInit(interp, sname);
676 while(1){
677 struct timeval tv;
678 fd_set rfd;
680 FD_ZERO(&rfd);
681 FD_SET(s, &rfd);
682 tv.tv_sec = (gPEAbandonTimeout) ? gPEAbandonTimeout : gPeInputTimeout;
683 tv.tv_usec = 0;
684 if((n = select(s+1, &rfd, 0, 0, &tv)) > 0){
685 socklen_t ll = l;
687 gPEAbandonTimeout = 0;
689 if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){
690 dprint((SYSDBG_ERR, "accept failure: %s",
691 error_description(errno)));
692 break;
695 dprint((5, "accept success: %d", cs));
698 * tcl commands are prefixed with a number representing
699 * the length of the command string and a newline character.
700 * the characters representing the length and the newline
701 * are not included in the command line length calculation.
703 o = co = 0;
704 while((n = read(cs, buf + o, bl - o - 1)) > 0){
705 o += n;
706 if(!co){
707 int i, x = 0;
709 for(i = 0; i < o; i++)
710 if(buf[i] == '\n'){
711 co = ++i;
712 l = x + co;
713 if(bl < l + 1){
714 bl = l + 1;
715 fs_resize((void **) &buf, bl * sizeof(char));
718 break;
720 else
721 x = (x * 10) + (buf[i] - '0');
724 if(o && o == l)
725 break;
728 if(n == 0){
729 dprint((SYSDBG_ERR, "read EOF"));
731 else if(n < 0){
732 dprint((SYSDBG_ERR, "read failure: %s", error_description(errno)));
734 else{
735 buf[o] = '\0';
737 /* Log every Eval if somebody *really* wants to see it. */
738 if(debug > 6){
739 char dbuf[5120];
740 int dlim = (debug >= 9) ? 256 : 5120 - 32;
742 snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]);
744 /* But DON'T log any clear-text credentials */
745 if(dbuf[9] == 'P'
746 && dbuf[10] == 'E'
747 && dbuf[11] == 'S'
748 && !strncmp(dbuf + 12, "ession creds ", 13)){
749 char *p;
751 for(p = &dbuf[25]; *p; p++)
752 *p = 'X';
755 dprint((1, dbuf));
758 switch(Tcl_Eval(interp, &buf[co])){
759 case TCL_OK : peReturn(cs, "OK", Tcl_GetStringResult(interp)); break;
760 case TCL_ERROR : peReturn(cs, "ERROR", Tcl_GetStringResult(interp)); break;
761 case TCL_BREAK : peReturn(cs, "BREAK", Tcl_GetStringResult(interp)); break;
762 case TCL_RETURN : peReturn(cs, "RETURN", Tcl_GetStringResult(interp)); break;
763 default : peReturn(cs, "BOGUS", "eval returned unexpected value"); break;
767 close(cs);
769 else if(errno != EINTR){
770 if(n < 0){
771 dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno)));
773 else{
774 dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec));
777 Tcl_Exit(0);
779 /* Tcl_Exit should never return. Getting here is an error. */
780 dprint((SYSDBG_ERR, "Tcl_Exit failure"));
784 else
785 perror("listen");
787 else
788 perror("bind");
790 close(s);
791 unlink(sname);
793 else
794 perror("socket");
796 else
797 fprintf(stderr, "Can't read socket name\n");
799 exit(ev);
804 * peReturn - common routine to return TCL result
806 void
807 peReturn(int sock, char *status, const char *result)
809 if(peWrite(sock, status))
810 if(peWrite(sock, "\n"))
811 peWrite(sock, (char *) result);
815 * peWrite - write all the given string on the given socket
818 peWrite(int sock, char *s)
820 int i, n;
822 for(i = 0, n = strlen(s); n; n = n - i)
823 if((i = write(sock, s + i, n)) < 0){
824 dprint((SYSDBG_ERR, "write: %s", error_description(errno)));
825 return(0);
828 return(1);
832 * PEInit - Initialize exported TCL functions
835 PEInit(Tcl_Interp *interp, char *sname)
837 dprint((2, "PEInit: %s", sname));
839 if(Tcl_Init(interp) == TCL_ERROR) {
840 return(TCL_ERROR);
843 Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd,
844 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
846 Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd,
847 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
849 Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd,
850 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
852 Tcl_CreateObjCommand(interp, "PESession", PESessionCmd,
853 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
855 Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd,
856 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
858 Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd,
859 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
861 Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd,
862 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
864 Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd,
865 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
867 Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd,
868 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
870 Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd,
871 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
873 Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd,
874 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
876 Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd,
877 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
879 Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd,
880 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
882 Tcl_CreateObjCommand(interp, "PERss", PERssCmd,
883 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
885 Tcl_CreateExitHandler(PEExitCleanup, sname);
887 #ifdef ENABLE_LDAP
888 wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
889 wpldap_global->query_no = 0;
890 wpldap_global->ldap_search_list = NULL;
891 #endif /* ENABLE_LDAP */
893 return(TCL_OK);
897 void
898 PEExitCleanup(ClientData clientData)
900 dprint((4, "PEExitCleanup"));
902 if(wps_global){
903 /* destroy any open stream */
904 peDestroyStream(wps_global);
906 /* destroy user context */
907 peDestroyUserContext(&wps_global);
910 #ifdef ENABLE_LDAP
911 if(wpldap_global){
912 if(wpldap_global->ldap_search_list)
913 free_wpldapres(wpldap_global->ldap_search_list);
914 fs_give((void **)&wpldap_global);
916 #endif /* ENABLE_LDAP */
918 if((char *) clientData)
919 unlink((char *) clientData);
921 peFreeAttach(&peCompAttach);
923 dprint((SYSDBG_INFO, "finished"));
928 * PEInfoCmd - export various bits of alpine state
931 PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
933 char *err = "Unknown PEInfo request";
935 dprint((2, "PEInfoCmd"));
937 if(objc == 1){
938 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
940 else{
941 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
943 if(s1){
944 if(!strcmp(s1, "colorset")){
945 char *varname, *fghex, *bghex;
946 char tvname[256], asciicolor[256];
947 struct variable *vtmp;
948 Tcl_Obj **cObj;
949 int cObjc;
950 SPEC_COLOR_S *hcolors, *thc;
952 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
953 Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
954 return(TCL_ERROR);
957 if(!strcmp(varname, "viewer-hdr-colors")){
958 char *newhdr = NULL, *newpat = NULL, *utype;
959 int hindex, i;
961 if(objc < 5){
962 Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
963 return(TCL_ERROR);
966 hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
967 if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
968 Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
969 return(TCL_ERROR);
972 if(!strcmp(utype, "delete")){
973 if(!hcolors){
974 Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
975 return(TCL_ERROR);
978 if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
979 Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
980 return(TCL_ERROR);
983 if(hindex == 0){
984 thc = hcolors;
985 hcolors = hcolors->next;
986 thc->next = NULL;
987 free_spec_colors(&thc);
989 else{
990 /* zero based */
991 for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
994 if(thc && thc->next){
995 SPEC_COLOR_S *thc2 = thc->next;
997 thc->next = thc2->next;
998 thc2->next = NULL;
999 free_spec_colors(&thc2);
1001 else{
1002 Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
1003 return(TCL_ERROR);
1007 else if(!strcmp(utype, "add")){
1008 if(objc != 6){
1009 Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
1010 return(TCL_ERROR);
1013 if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
1014 return (TCL_ERROR);
1016 if(cObjc != 2){
1017 Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
1018 return(TCL_ERROR);
1021 newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
1022 newpat = Tcl_GetStringFromObj(cObj[1], NULL);
1023 if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
1024 return (TCL_ERROR);
1026 if(cObjc != 2){
1027 Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
1028 return(TCL_ERROR);
1031 fghex = Tcl_GetStringFromObj(cObj[0], NULL);
1032 bghex = Tcl_GetStringFromObj(cObj[1], NULL);
1033 if(newhdr && newpat && fghex && bghex){
1034 SPEC_COLOR_S **hcp;
1036 for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
1039 *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
1040 (*hcp)->inherit = 0;
1041 (*hcp)->spec = cpystr(newhdr);
1042 (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
1043 (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
1045 if(newpat && *newpat)
1046 (*hcp)->val = string_to_pattern(newpat);
1047 else
1048 (*hcp)->val = NULL;
1050 (*hcp)->next = NULL;
1052 else{
1053 Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
1054 return(TCL_ERROR);
1057 else if(!strcmp(utype, "update")){
1058 if(objc != 6){
1059 Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
1060 return(TCL_ERROR);
1063 if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
1064 && cObjc == 3
1065 && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
1066 && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
1067 && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
1068 Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
1069 return (TCL_ERROR);
1072 if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
1073 && cObjc == 2
1074 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
1075 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
1076 Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
1077 return (TCL_ERROR);
1080 for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
1083 if(!thc){
1084 Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
1085 return (TCL_ERROR);
1088 if(thc->spec)
1089 fs_give((void **)&thc->spec);
1091 thc->spec = cpystr(newhdr);
1092 if(ascii_colorstr(asciicolor, fghex) == 0) {
1093 if(thc->fg)
1094 fs_give((void **)&thc->fg);
1096 thc->fg = cpystr(asciicolor);
1098 else{
1099 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
1100 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1101 return(TCL_ERROR);
1104 if(ascii_colorstr(asciicolor, bghex) == 0) {
1105 if(thc->bg)
1106 fs_give((void **)&thc->bg);
1108 thc->bg = cpystr(asciicolor);
1110 else{
1111 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1112 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1113 return(TCL_ERROR);
1116 if(thc->val)
1117 fs_give((void **)&thc->val);
1119 if(newpat && *newpat){
1120 thc->val = string_to_pattern(newpat);
1123 else{
1124 Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
1125 return(TCL_ERROR);
1128 vtmp = &wps_global->vars[V_VIEW_HDR_COLORS];
1129 for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
1130 fs_give((void **)&vtmp->main_user_val.l[i]);
1132 if(vtmp->main_user_val.l)
1133 fs_give((void **)&vtmp->main_user_val.l);
1135 vtmp->main_user_val.l = varlist_from_spec_colors(hcolors);
1136 set_current_val(vtmp, FALSE, FALSE);
1137 free_spec_colors(&hcolors);
1138 return(TCL_OK);
1140 else {
1141 if(objc != 4){
1142 Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
1143 return(TCL_ERROR);
1146 if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
1147 && cObjc == 2
1148 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
1149 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
1150 Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
1151 return (TCL_ERROR);
1154 snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
1155 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1156 vtmp->name && strucmp(vtmp->name, tvname);
1157 vtmp++)
1160 if(!vtmp->name || vtmp->is_list){
1161 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1162 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1163 return(TCL_ERROR);
1166 if(ascii_colorstr(asciicolor, fghex) == 0) {
1167 if(vtmp->main_user_val.p)
1168 fs_give((void **)&vtmp->main_user_val.p);
1170 vtmp->main_user_val.p = cpystr(asciicolor);
1171 set_current_val(vtmp, FALSE, FALSE);
1172 if(!strucmp(varname, "normal"))
1173 pico_set_fg_color(asciicolor);
1175 else{
1176 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
1177 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1178 return(TCL_ERROR);
1181 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
1182 vtmp++;
1183 if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
1184 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1185 vtmp->name && strucmp(vtmp->name, tvname);
1186 vtmp++)
1189 if(!vtmp->name || vtmp->is_list){
1190 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
1191 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1192 return(TCL_ERROR);
1195 if(ascii_colorstr(asciicolor, bghex) == 0) {
1196 if(vtmp->main_user_val.p)
1197 fs_give((void **)&vtmp->main_user_val.p);
1199 vtmp->main_user_val.p = cpystr(asciicolor);
1200 set_current_val(vtmp, FALSE, FALSE);
1201 if(!strucmp(varname, "normal"))
1202 pico_set_bg_color(asciicolor);
1204 else{
1205 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
1206 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
1207 return(TCL_ERROR);
1210 Tcl_SetResult(interp, "1", TCL_STATIC);
1211 return(TCL_OK);
1214 else if(!strcmp(s1, "lappend")){
1215 if(objc >= 4){
1216 Tcl_Obj *dObj;
1217 int i;
1219 if((dObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
1220 for(i = 3; i < objc; i++)
1221 if(Tcl_ListObjAppendElement(interp, dObj, objv[i]) != TCL_OK)
1222 return(TCL_ERROR);
1224 if(i == objc){
1225 return(TCL_OK);
1228 else
1229 err = "PEInfo lappend: Unknown list name";
1231 else
1232 err = "PEInfo lappend: Too few args";
1234 else if(objc == 2){
1235 if(!strcmp(s1, "version")){
1236 char buf[256];
1239 * CMD: version
1241 * Returns: string representing Pine version
1242 * engine built on
1244 Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC);
1245 return(TCL_OK);
1247 else if(!strcmp(s1, "revision")){
1248 char buf[16];
1251 * CMD: revision
1253 * Returns: string representing Pine SVN revision
1254 * engine built on
1257 Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE);
1258 return(TCL_OK);
1260 else if(!strcmp(s1, "key")){
1261 static char key[64];
1263 if(!key[0])
1264 peRandomString(key,32,PRS_UPPER_CASE);
1266 Tcl_SetResult(interp, key, TCL_STATIC);
1267 return(TCL_OK);
1269 else if(!strcmp(s1, "indexheight")){
1270 Tcl_SetResult(interp, wps_global->VAR_WP_INDEXHEIGHT ?
1271 wps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE);
1272 return(TCL_OK);
1274 else if(!strcmp(s1, "indexlines")){
1275 Tcl_SetResult(interp, wps_global->VAR_WP_INDEXLINES ?
1276 wps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE);
1277 return(TCL_OK);
1279 else if(!strcmp(s1, "aggtabstate")){
1280 Tcl_SetResult(interp, wps_global->VAR_WP_AGGSTATE ?
1281 wps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE);
1282 return(TCL_OK);
1284 else if(!strcmp(s1, "alpinestate")){
1285 char *wps, *p, *q;
1287 if((wps = wps_global->VAR_WP_STATE) != NULL){
1288 wps = p = q = cpystr(wps);
1290 if(*q == '\\' && *(q+1) == '$')
1291 q++;
1292 while((*p++ = *q++) != '\0');
1295 Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE);
1297 if(wps)
1298 fs_give((void **) &wps);
1300 return(TCL_OK);
1302 else if(!strcmp(s1, "foreground")){
1303 char *color;
1305 if(!((color = pico_get_last_fg_color())
1306 && (color = color_to_asciirgb(color))
1307 && (color = peColorStr(color,wtmp_20k_buf))))
1308 color = "000000";
1310 Tcl_SetResult(interp, color, TCL_VOLATILE);
1311 return(TCL_OK);
1313 else if(!strcmp(s1, "background")){
1314 char *color;
1316 if(!((color = pico_get_last_bg_color())
1317 && (color = color_to_asciirgb(color))
1318 && (color = peColorStr(color,wtmp_20k_buf))))
1319 color = "FFFFFF";
1321 Tcl_SetResult(interp, color, TCL_VOLATILE);
1322 return(TCL_OK);
1324 else if(!strcmp(s1, "flaglist")){
1325 int i;
1326 char *p;
1327 Tcl_Obj *itemObj;
1330 * BUG: This list should get merged with the static list in "cmd_flag"
1331 * and exported via some function similar to "feature_list()"
1333 static char *flag_list[] = {
1334 "Important", "New", "Answered", "Deleted", NULL
1338 * CMD: flaglist
1340 * Returns: list of FLAGS available for setting
1342 for(i = 0; (p = flag_list[i]); i++)
1343 if((itemObj = Tcl_NewStringObj(p, -1)) != NULL){
1344 if(Tcl_ListObjAppendElement(interp,
1345 Tcl_GetObjResult(interp),
1346 itemObj) != TCL_OK)
1350 return(TCL_OK);
1352 else if(!strcmp(s1, "featurelist")){
1353 int i;
1354 char *curfeature, *s;
1355 FEATURE_S *feature;
1356 Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
1359 * CMD: featurelist
1361 * Returns: list of FEATURES available for setting
1363 for(i = 0, curfeature = NULL; (feature = feature_list(i)); i++)
1364 if((s = feature_list_section(feature)) != NULL){
1365 if(!curfeature || strucmp(s, curfeature)){
1366 if(resObj) {
1367 Tcl_ListObjAppendElement(interp,
1368 secObj,
1369 resObj);
1370 Tcl_ListObjAppendElement(interp,
1371 Tcl_GetObjResult(interp),
1372 secObj);
1375 secObj = Tcl_NewListObj(0, NULL);
1376 resObj = Tcl_NewListObj(0, NULL);
1377 if(Tcl_ListObjAppendElement(interp,
1378 secObj,
1379 Tcl_NewStringObj(s,-1)) != TCL_OK)
1382 curfeature = s;
1385 if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1386 if(Tcl_ListObjAppendElement(interp,
1387 resObj,
1388 itemObj) != TCL_OK)
1393 if(resObj){
1394 Tcl_ListObjAppendElement(interp, secObj, resObj);
1395 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj);
1398 return(TCL_OK);
1400 else if(!strcmp(s1, "featuresettings")){
1401 int i;
1402 FEATURE_S *feature;
1403 Tcl_Obj *itemObj;
1406 * CMD: featuresettings
1408 * Returns: list of FEATURES currently SET
1410 for(i = 0; (feature = feature_list(i)); i++)
1411 if(feature_list_section(feature)){
1412 if(F_ON(feature->id, wps_global)){
1413 if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
1414 if(Tcl_ListObjAppendElement(interp,
1415 Tcl_GetObjResult(interp),
1416 itemObj) != TCL_OK)
1422 return(TCL_OK);
1424 else if(!strcmp(s1, "signature")){
1425 char *sig;
1427 if((wps_global->VAR_LITERAL_SIG
1428 || (wps_global->VAR_SIGNATURE_FILE
1429 && IS_REMOTE(wps_global->VAR_SIGNATURE_FILE)))
1430 && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){
1431 char *p, *q;
1433 for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1434 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1435 Tcl_NewStringObj(p, q - p));
1437 if(*p)
1438 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1439 Tcl_NewStringObj(p, -1));
1441 fs_give((void **) &sig);
1443 else
1444 Tcl_SetResult(interp, "", TCL_STATIC);
1446 return(TCL_OK);
1448 else if(!strcmp(s1, "rawsig")){
1449 char *err = NULL, *sig = NULL, *p, *q;
1451 if(wps_global->VAR_LITERAL_SIG){
1452 char *err = NULL;
1453 char **apval;
1455 if(wps_global->restricted){
1456 err = "Alpine demo can't change config file";
1458 else{
1459 /* BUG: no "exceptions file" support */
1460 if((apval = APVAL(&wps_global->vars[V_LITERAL_SIG], Main)) != NULL){
1461 sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
1462 sig[0] = '\0';
1463 cstring_to_string(*apval, sig);
1465 else
1466 err = "Problem accessing configuration";
1469 else if(!IS_REMOTE(wps_global->VAR_SIGNATURE_FILE))
1470 snprintf(err = wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
1471 wps_global->VAR_SIGNATURE_FILE ? wps_global->VAR_SIGNATURE_FILE : "<null>");
1472 else if(!(sig = simple_read_remote_file(wps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))
1473 err = "Can't read remote pinerc";
1475 if(err){
1476 Tcl_SetResult(interp, err, TCL_VOLATILE);
1477 return(TCL_ERROR);
1480 for(p = sig; (q = strindex(p, '\n')); p = q + 1)
1481 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1482 Tcl_NewStringObj(p, q - p));
1484 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1485 Tcl_NewStringObj(p, -1));
1486 fs_give((void **) &sig);
1487 return(TCL_OK);
1489 else if(!strcmp(s1, "statmsg")){
1490 char *s = sml_getmsg();
1491 /* BUG: can this be removed? */
1493 Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE);
1494 return(TCL_OK);
1496 else if(!strcmp(s1, "statmsgs")){
1497 char **s = sml_getmsgs();
1498 char **tmps, *lmsg = NULL;
1500 for(tmps = s; tmps && *tmps; lmsg = *tmps++)
1501 if(!lmsg || strcmp(lmsg, *tmps))
1502 Tcl_ListObjAppendElement(interp,
1503 Tcl_GetObjResult(interp),
1504 Tcl_NewStringObj(*tmps, -1));
1506 fs_give((void **)&s);
1507 return(TCL_OK);
1509 else if(!strcmp(s1, "saveconf")){
1510 write_pinerc(wps_global, Main, WRP_NOUSER);
1511 return(TCL_OK);
1513 else if(!strucmp(s1, "sort")){
1514 return(peAppendDefaultSort(interp));
1516 else if(!strcmp(s1, "ldapenabled")){
1518 * CMD: ldapenabled
1520 * Returns: 1 if enabled 0 if not
1522 #ifdef ENABLE_LDAP
1523 Tcl_SetResult(interp, "1", TCL_VOLATILE);
1524 #else
1525 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1526 #endif
1528 return(TCL_OK);
1530 else if(!strcmp(s1, "prunecheck")){
1531 time_t now;
1532 struct tm *tm_now;
1533 char tmp[50];
1535 if(!check_prune_time(&now, &tm_now)){
1536 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1537 return(TCL_OK);
1538 } else {
1540 * We're going to reset the last-time-pruned variable
1541 * so that it asks a maximum of 1 time per month.
1542 * PROs: Annoying-factor is at its lowest
1543 * Can go ahead and move folders right away if
1544 * pruning-rule is automatically set to do so
1545 * CONs: Annoying-factor is at its lowest, if it's set
1546 * later then we can ensure that the questions
1547 * actually get answered or it will keep asking
1549 wps_global->last_expire_year = tm_now->tm_year;
1550 wps_global->last_expire_month = tm_now->tm_mon;
1551 snprintf(tmp, sizeof(tmp), "%d.%d", wps_global->last_expire_year,
1552 wps_global->last_expire_month + 1);
1553 set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main);
1555 Tcl_SetResult(interp, "1", TCL_VOLATILE);
1557 return(TCL_OK);
1559 else if(!strcmp(s1, "prunetime")){
1560 time_t now;
1561 struct tm *tm_now;
1562 CONTEXT_S *prune_cntxt;
1563 Tcl_Obj *retObj = NULL;
1564 int cur_month, ok = 1;
1565 char **p;
1566 static int moved_fldrs = 0;
1568 now = time((time_t *)0);
1569 tm_now = localtime(&now);
1570 cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
1572 if(!(prune_cntxt = default_save_context(wps_global->context_list)))
1573 prune_cntxt = wps_global->context_list;
1575 if(prune_cntxt){
1576 if(wps_global->VAR_DEFAULT_FCC && *wps_global->VAR_DEFAULT_FCC
1577 && context_isambig(wps_global->VAR_DEFAULT_FCC))
1578 if((retObj = wp_prune_folders(prune_cntxt,
1579 wps_global->VAR_DEFAULT_FCC,
1580 cur_month, "sent",
1581 wps_global->pruning_rule, &ok,
1582 moved_fldrs, interp)) != NULL)
1583 Tcl_ListObjAppendElement(interp,
1584 Tcl_GetObjResult(interp),
1585 retObj);
1587 if(ok && wps_global->VAR_READ_MESSAGE_FOLDER
1588 && *wps_global->VAR_READ_MESSAGE_FOLDER
1589 && context_isambig(wps_global->VAR_READ_MESSAGE_FOLDER))
1590 if((retObj = wp_prune_folders(prune_cntxt,
1591 wps_global->VAR_READ_MESSAGE_FOLDER,
1592 cur_month, "read",
1593 wps_global->pruning_rule, &ok,
1594 moved_fldrs, interp)) != NULL)
1595 Tcl_ListObjAppendElement(interp,
1596 Tcl_GetObjResult(interp),
1597 retObj);
1598 if(ok && (p = wps_global->VAR_PRUNED_FOLDERS)){
1599 for(; ok && *p; p++)
1600 if(**p && context_isambig(*p))
1601 if((retObj = wp_prune_folders(prune_cntxt,
1602 *p, cur_month, "",
1603 wps_global->pruning_rule, &ok,
1604 moved_fldrs, interp)) != NULL)
1605 Tcl_ListObjAppendElement(interp,
1606 Tcl_GetObjResult(interp),
1607 retObj);
1610 moved_fldrs = 1;
1611 return(TCL_OK);
1613 else if(!strcmp(s1, "authrequestor")){
1614 Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC);
1615 return(TCL_OK);
1617 else if(!strcmp(s1, "noop")){
1618 /* tickle the imap server too */
1619 if(wps_global->mail_stream)
1620 pine_mail_ping(wps_global->mail_stream);
1622 Tcl_SetResult(interp, "NOOP", TCL_STATIC);
1623 return(TCL_OK);
1625 else if(!strcmp(s1, "inputtimeout")){
1626 Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE);
1627 return(TCL_OK);
1630 else if(objc == 3){
1631 if(!strcmp(s1, "feature")){
1632 char *featurename;
1633 int i, isset = 0;
1634 FEATURE_S *feature;
1637 * CMD: feature
1639 * ARGS: featurename -
1641 * Returns: 1 if named feature set, 0 otherwise
1644 if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
1645 for(i = 0; (feature = feature_list(i)); i++)
1646 if(!strucmp(featurename, feature->name)){
1647 isset = F_ON(feature->id, wps_global);
1648 break;
1651 Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE);
1652 return(TCL_OK);
1654 else if(!strcmp(s1, "colorget")){
1655 char *varname;
1656 char tvname[256], hexcolor[256];
1657 struct variable *vtmp;
1658 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
1659 return(TCL_ERROR);
1661 if(strcmp("viewer-hdr-colors", varname) == 0){
1662 SPEC_COLOR_S *hcolors, *thc;
1663 Tcl_Obj *resObj;
1664 char hexcolor[256], *tstr = NULL;
1666 hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
1667 for(thc = hcolors; thc; thc = thc->next){
1668 resObj = Tcl_NewListObj(0,NULL);
1669 Tcl_ListObjAppendElement(interp, resObj,
1670 Tcl_NewStringObj(thc->spec, -1));
1671 hex_colorstr(hexcolor, thc->fg);
1672 Tcl_ListObjAppendElement(interp, resObj,
1673 Tcl_NewStringObj(hexcolor, -1));
1674 hex_colorstr(hexcolor, thc->bg);
1675 Tcl_ListObjAppendElement(interp, resObj,
1676 Tcl_NewStringObj(hexcolor, -1));
1677 Tcl_ListObjAppendElement(interp, resObj,
1678 Tcl_NewStringObj(thc->val
1679 ? tstr = pattern_to_string(thc->val)
1680 : "", -1));
1681 if(tstr) fs_give((void **)&tstr);
1682 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1683 resObj);
1685 fs_give((void **)&hcolors);
1686 return(TCL_OK);
1688 else {
1689 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
1690 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1691 vtmp->name && strucmp(vtmp->name, tvname);
1692 vtmp++);
1693 if(!vtmp->name) return(TCL_ERROR);
1694 if(vtmp->is_list) return(TCL_ERROR);
1695 if(!vtmp->current_val.p)
1696 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1697 Tcl_NewStringObj("", -1));
1698 else{
1699 hex_colorstr(hexcolor, vtmp->current_val.p);
1700 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1701 Tcl_NewStringObj(hexcolor, -1));
1703 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
1704 vtmp++;
1705 if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
1706 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
1707 vtmp->name && strucmp(vtmp->name, tvname);
1708 vtmp++)
1711 if(!vtmp->name) return(TCL_ERROR);
1712 if(vtmp->is_list) return(TCL_ERROR);
1713 if(!vtmp->current_val.p)
1714 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1715 Tcl_NewStringObj("", -1));
1716 else{
1717 hex_colorstr(hexcolor, vtmp->current_val.p);
1718 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
1719 Tcl_NewStringObj(hexcolor, -1));
1722 return(TCL_OK);
1724 else if(!strcmp(s1, "varget")){
1725 struct variable *vtmp;
1726 Tcl_Obj *itemObj, *resObj, *secObj;
1727 char *vallist, *varname, tmperrmsg[256];
1728 int i;
1729 NAMEVAL_S *tmpnv;
1732 * CMD: varget
1734 * Returns: get the values for the requested variable
1736 * The list returned follows this general form:
1738 * char *; variable name
1739 * char **; list of set values
1740 * char *; display type (listbox, text, textarea, ...)
1741 * char **; list of possible values
1742 * (so far this is only useful for listboxes)
1744 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
1745 Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj",
1746 TCL_VOLATILE);
1747 return(TCL_ERROR);
1750 for(vtmp = wps_global->vars;
1751 vtmp->name && strucmp(vtmp->name, varname);
1752 vtmp++)
1755 if(!vtmp->name){
1756 snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
1757 strlen(varname) < 200 ? varname : "");
1758 Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
1759 return(TCL_ERROR);
1761 if((itemObj = Tcl_NewStringObj(vtmp->name, -1)) != NULL){
1762 Tcl_ListObjAppendElement(interp,
1763 Tcl_GetObjResult(interp),
1764 itemObj);
1765 resObj = Tcl_NewListObj(0, NULL);
1766 if(vtmp->is_list){
1767 for(i = 0 ; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
1768 vallist = vtmp->current_val.l[i];
1769 if(*(vallist))
1770 itemObj = Tcl_NewStringObj(vallist, -1);
1771 else
1772 itemObj = Tcl_NewStringObj("", -1);
1773 Tcl_ListObjAppendElement(interp, resObj, itemObj);
1776 else{
1777 itemObj = Tcl_NewStringObj(vtmp->current_val.p ?
1778 vtmp->current_val.p : "", -1);
1779 Tcl_ListObjAppendElement(interp, resObj, itemObj);
1781 Tcl_ListObjAppendElement(interp,
1782 Tcl_GetObjResult(interp),
1783 resObj);
1784 secObj = Tcl_NewListObj(0, NULL);
1785 if(vtmp->is_list)
1786 itemObj = Tcl_NewStringObj("textarea", -1);
1787 else{
1788 NAMEVAL_S *(*tmpf)(int);
1789 switch(vtmp - wps_global->vars){
1790 case V_SAVED_MSG_NAME_RULE:
1791 tmpf = save_msg_rules;
1792 break;
1793 case V_FCC_RULE:
1794 tmpf = fcc_rules;
1795 break;
1796 case V_SORT_KEY:
1797 tmpf = sort_key_rules;
1798 break;
1799 case V_AB_SORT_RULE:
1800 tmpf = ab_sort_rules;
1801 break;
1802 case V_FLD_SORT_RULE:
1803 tmpf = fld_sort_rules;
1804 break;
1805 case V_GOTO_DEFAULT_RULE:
1806 tmpf = goto_rules;
1807 break;
1808 case V_INCOMING_STARTUP:
1809 tmpf = incoming_startup_rules;
1810 break;
1811 case V_PRUNING_RULE:
1812 tmpf = pruning_rules;
1813 break;
1814 case V_WP_INDEXHEIGHT:
1815 tmpf = wp_indexheight_rules;
1816 break;
1817 default:
1818 tmpf = NULL;
1819 break;
1821 if(tmpf){
1822 for(i = 0; (tmpnv = (tmpf)(i)); i++){
1823 itemObj = Tcl_NewListObj(0, NULL);
1824 Tcl_ListObjAppendElement(interp, itemObj,
1825 Tcl_NewStringObj(tmpnv->name, -1));
1826 if(tmpnv->shortname)
1827 Tcl_ListObjAppendElement(interp, itemObj,
1828 Tcl_NewStringObj(tmpnv->shortname, -1));
1829 Tcl_ListObjAppendElement(interp, secObj, itemObj);
1831 itemObj = Tcl_NewStringObj("listbox", -1);
1833 else
1834 itemObj = Tcl_NewStringObj("text", -1);
1836 Tcl_ListObjAppendElement(interp,
1837 Tcl_GetObjResult(interp),
1838 itemObj);
1839 Tcl_ListObjAppendElement(interp,
1840 Tcl_GetObjResult(interp),
1841 secObj);
1843 return(TCL_OK);
1845 else if(!strcmp(s1, "rawsig")){
1847 if(wps_global->VAR_LITERAL_SIG){
1848 char *cstring_version, *sig, *line;
1849 int i, nSig;
1850 Tcl_Obj **objSig;
1852 wtmp_20k_buf[0] = '\0';
1853 Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
1854 for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
1855 if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
1856 snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
1858 sig = cpystr(wtmp_20k_buf);
1860 if((cstring_version = string_to_cstring(sig)) != NULL){
1861 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, Main);
1862 fs_give((void **)&cstring_version);
1865 fs_give((void **) &sig);
1866 return(TCL_OK);
1868 else
1869 return(peWriteSig(interp, wps_global->VAR_SIGNATURE_FILE,
1870 &((Tcl_Obj **)objv)[2]));
1872 else if(!strcmp(s1, "statmsg")){
1873 char *msg;
1876 * CMD: statmsg
1878 * ARGS: msg - text to set
1880 * Returns: nothing, but with global status message
1881 * buf set to given msg
1884 if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
1885 sml_addmsg(0, msg);
1887 return(TCL_OK);
1889 else if(!strcmp(s1, "mode")){
1890 char *mode;
1891 int rv = 0;
1894 * CMD: mode
1896 * ARGS: <mode>
1898 * Returns: return value of given binary mode
1901 if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1902 if(!strcmp(mode, "full-header-mode"))
1903 rv = wps_global->full_header;
1906 Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
1907 return(TCL_OK);
1909 else if(!strcmp(s1, "indexlines")){
1910 int n;
1911 char *p;
1913 if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
1914 set_variable(V_WP_INDEXLINES, p = int2string(n), 0, 0, Main);
1915 Tcl_SetResult(interp, p, TCL_VOLATILE);
1917 return(TCL_OK);
1919 else if(!strcmp(s1, "aggtabstate")){
1920 int n;
1921 char *p;
1923 if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
1924 set_variable(V_WP_AGGSTATE, p = int2string(n), 0, 0, Main);
1925 Tcl_SetResult(interp, p, TCL_VOLATILE);
1927 return(TCL_OK);
1929 else if(!strcmp(s1, "alpinestate")){
1930 char *wps, *p, *q, *twps = NULL;
1931 int dollars = 0;
1933 if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
1934 for(p = wps; *p; p++)
1935 if(*p == '$')
1936 dollars++;
1938 if(dollars){
1939 twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char));
1940 p = wps;
1941 q = twps;
1943 if(*p == '$')
1944 *q++ = '\\';
1946 while((*q++ = *p++) != '\0');
1949 set_variable(V_WP_STATE, twps ? twps : wps, 0, 1, Main);
1950 Tcl_SetResult(interp, wps, TCL_VOLATILE);
1951 if(twps)
1952 fs_give((void **) &twps);
1955 return(TCL_OK);
1957 else if(!strcmp(s1, "set")){
1958 Tcl_Obj *rObj;
1960 if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
1961 Tcl_SetObjResult(interp, rObj);
1962 return(TCL_OK);
1964 else
1965 return(TCL_ERROR);
1967 else if(!strcmp(s1, "unset")){
1968 char *varname;
1970 return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR);
1973 else if(objc == 4){
1974 if(!strcmp(s1, "feature")){
1975 char *featurename;
1976 int i, set, wasset = 0;
1977 FEATURE_S *feature;
1980 * CMD: feature
1982 * ARGS: featurename -
1983 * value - new value to assign flag
1985 * Returns: 1 if named feature set, 0 otherwise
1988 if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
1989 && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
1990 for(i = 0; (feature = feature_list(i)); i++)
1991 if(!strucmp(featurename, feature->name)){
1992 if(set != F_ON(feature->id, wps_global)){
1993 toggle_feature(wps_global,
1994 &wps_global->vars[V_FEATURE_LIST],
1995 feature, TRUE, Main);
1997 if(wps_global->prc)
1998 wps_global->prc->outstanding_pinerc_changes = 1;
2001 break;
2004 Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
2005 return(TCL_OK);
2007 else if(!strucmp(s1, "help")){
2008 HelpType text;
2009 int i;
2010 char **help_text, **ptext, *helpname, tmperrmsg[256],
2011 *function;
2012 Tcl_Obj *itemObj;
2013 struct variable *vtmp;
2014 FEATURE_S *ftmp;
2016 if(!(helpname = Tcl_GetStringFromObj(objv[2], NULL))){
2017 Tcl_SetResult(interp,
2018 "Can't Tcl_GetStringFromObj for helpname",
2019 TCL_VOLATILE);
2020 return(TCL_ERROR);
2022 if(!(function = Tcl_GetStringFromObj(objv[3], NULL))){
2023 Tcl_SetResult(interp,
2024 "Can't Tcl_GetStringFromObj for function",
2025 TCL_VOLATILE);
2026 return(TCL_ERROR);
2028 if(strucmp(function, "plain") == 0){
2029 if((text = help_name2section(helpname, strlen(helpname)))
2030 == NO_HELP)
2031 return(TCL_OK);
2033 else if(strucmp(function, "variable") == 0){
2034 for(vtmp = wps_global->vars;
2035 vtmp->name && strucmp(vtmp->name, helpname);
2036 vtmp++);
2037 if(!vtmp->name) {
2038 snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
2039 strlen(helpname) < 200 ? helpname : "");
2040 Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
2041 return(TCL_ERROR);
2043 text = config_help(vtmp - wps_global->vars, 0);
2044 if(text == NO_HELP)
2045 return(TCL_OK);
2047 else if(strucmp(function, "feature") == 0){
2048 for(i = 0; (ftmp = feature_list(i)); i++){
2049 if(!strucmp(helpname, ftmp->name)){
2050 text = ftmp->help;
2051 break;
2054 if(!ftmp || text == NO_HELP){
2055 return(TCL_OK);
2058 else {
2059 snprintf(tmperrmsg, sizeof(tmperrmsg), "Invalid function: %s",
2060 strlen(helpname) < 200 ? function : "");
2061 Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
2062 return(TCL_ERROR);
2064 /* assumption here is that HelpType is char ** */
2065 help_text = text;
2066 for(ptext = help_text; *ptext; ptext++){
2067 itemObj = Tcl_NewStringObj(*ptext, -1);
2068 Tcl_ListObjAppendElement(interp,
2069 Tcl_GetObjResult(interp),
2070 itemObj);
2072 return(TCL_OK);
2074 else if(!strcmp(s1, "varset")){
2075 char *varname, **tmpstrlist, *line;
2076 struct variable *vtmp;
2077 Tcl_Obj **objVal;
2078 int i, numlistvals = 0, strlistpos;
2080 if((varname = Tcl_GetStringFromObj(objv[2], NULL))
2081 && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
2082 &objVal) == TCL_OK)){
2083 for(vtmp = wps_global->vars;
2084 vtmp->name && strucmp(vtmp->name, varname);
2085 vtmp++);
2086 if(!vtmp->name){
2087 return(TCL_ERROR);
2089 else{
2090 /* found the variable */
2091 if(vtmp->is_list){
2092 for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
2093 fs_give((void **)&vtmp->main_user_val.l[i]);
2094 if(vtmp->main_user_val.l)
2095 fs_give((void **)&vtmp->main_user_val.l);
2096 if(numlistvals > 0){
2097 tmpstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
2098 for(i = 0, strlistpos = 0; i < numlistvals; i++){
2099 if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
2100 removing_leading_and_trailing_white_space(line);
2101 if(*line)
2102 tmpstrlist[strlistpos++] = cpystr(line);
2105 tmpstrlist[strlistpos] = NULL;
2106 vtmp->main_user_val.l = (char **)fs_get((strlistpos+1) *
2107 sizeof(char *));
2108 for(i = 0; i <= strlistpos; i++)
2109 vtmp->main_user_val.l[i] = tmpstrlist[i];
2110 fs_give((void **)&tmpstrlist);
2112 set_current_val(vtmp, FALSE, FALSE);
2113 return(TCL_OK);
2115 else{
2116 if((line = Tcl_GetStringFromObj(objVal[0], NULL)) != NULL){
2117 if(strucmp(vtmp->name, "reply-indent-string"))
2118 removing_leading_and_trailing_white_space(line);
2119 if(vtmp->main_user_val.p)
2120 fs_give((void **)&vtmp->main_user_val.p);
2121 if(*line)
2122 vtmp->main_user_val.p = cpystr(line);
2123 set_current_val(vtmp, FALSE, FALSE);
2124 return(TCL_OK);
2129 return(TCL_ERROR);
2131 else if(!strcmp(s1, "mode")){
2132 char *mode;
2133 int value, rv = 0;
2136 * CMD: mode
2138 * ARGS: <mode> <value>
2140 * Returns: old value of binary mode we were told to set
2143 if((mode = Tcl_GetStringFromObj(objv[2], NULL))
2144 && Tcl_GetIntFromObj(interp, objv[3], &value) != TCL_ERROR){
2145 if(!strcmp(mode, "full-header-mode")){
2146 rv = wps_global->full_header;
2147 wps_global->full_header = value;
2151 Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
2152 return(TCL_OK);
2154 else if(!strcmp(s1, "set")){
2155 Tcl_Obj *rObj;
2157 if((rObj = Tcl_ObjSetVar2(interp, objv[2], NULL, objv[3], TCL_LEAVE_ERR_MSG)) != NULL){
2158 Tcl_SetObjResult(interp, rObj);
2159 return(TCL_OK);
2161 else
2162 return(TCL_ERROR);
2165 else
2166 err = "PEInfo: Too many arguments";
2170 Tcl_SetResult(interp, err, TCL_STATIC);
2171 return(TCL_ERROR);
2176 * PEConfigCmd - edit various alpine config variables
2178 * The goal here is to remember what's changed, but not write to pinerc
2179 * until the user's actually chosen to save.
2182 PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
2184 char *err = "Unknown PEConfig request";
2185 char *s1;
2187 dprint((2, "PEConfigCmd"));
2189 if(objc == 1){
2190 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
2191 Tcl_SetResult(interp, err, TCL_STATIC);
2192 return(TCL_ERROR);
2194 s1 = Tcl_GetStringFromObj(objv[1], NULL);
2196 if(s1){
2197 if(!strcmp(s1, "colorset")){
2198 char *varname, *fghex, *bghex;
2199 char tvname[256], asciicolor[256];
2200 struct variable *vtmp;
2201 Tcl_Obj **cObj;
2202 int cObjc;
2203 SPEC_COLOR_S *hcolors, *thc;
2205 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
2206 Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
2207 return(TCL_ERROR);
2210 if(!strcmp(varname, "viewer-hdr-colors")){
2211 char *newhdr = NULL, *newpat = NULL, *utype;
2212 int hindex, i;
2214 if(objc < 5){
2215 Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
2216 return(TCL_ERROR);
2219 if(wps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
2220 hcolors = spec_colors_from_varlist(wps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
2221 else
2222 hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
2223 if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
2224 Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
2225 return(TCL_ERROR);
2228 if(!strcmp(utype, "delete")){
2229 if(!hcolors){
2230 Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
2231 return(TCL_ERROR);
2234 if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
2235 Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
2236 return(TCL_ERROR);
2239 if(hindex == 0){
2240 thc = hcolors;
2241 hcolors = hcolors->next;
2242 thc->next = NULL;
2243 free_spec_colors(&thc);
2245 else{
2246 /* zero based */
2247 for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
2250 if(thc && thc->next){
2251 SPEC_COLOR_S *thc2 = thc->next;
2253 thc->next = thc2->next;
2254 thc2->next = NULL;
2255 free_spec_colors(&thc2);
2257 else{
2258 Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
2259 return(TCL_ERROR);
2263 else if(!strcmp(utype, "add")){
2264 if(objc != 6){
2265 Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
2266 return(TCL_ERROR);
2269 if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
2270 return (TCL_ERROR);
2272 if(cObjc != 2){
2273 Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
2274 return(TCL_ERROR);
2277 newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
2278 newpat = Tcl_GetStringFromObj(cObj[1], NULL);
2279 if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
2280 return (TCL_ERROR);
2282 if(cObjc != 2){
2283 Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
2284 return(TCL_ERROR);
2287 fghex = Tcl_GetStringFromObj(cObj[0], NULL);
2288 bghex = Tcl_GetStringFromObj(cObj[1], NULL);
2289 if(newhdr && newpat && fghex && bghex){
2290 SPEC_COLOR_S **hcp;
2292 for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
2295 *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
2296 (*hcp)->inherit = 0;
2297 (*hcp)->spec = cpystr(newhdr);
2298 (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
2299 (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
2301 if(newpat && *newpat)
2302 (*hcp)->val = string_to_pattern(newpat);
2303 else
2304 (*hcp)->val = NULL;
2306 (*hcp)->next = NULL;
2308 else{
2309 Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
2310 return(TCL_ERROR);
2313 else if(!strcmp(utype, "update")){
2314 if(objc != 6){
2315 Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
2316 return(TCL_ERROR);
2319 if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
2320 && cObjc == 3
2321 && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
2322 && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
2323 && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
2324 Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
2325 return (TCL_ERROR);
2328 if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
2329 && cObjc == 2
2330 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
2331 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
2332 Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
2333 return (TCL_ERROR);
2336 for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
2339 if(!thc){
2340 Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
2341 return (TCL_ERROR);
2344 if(thc->spec)
2345 fs_give((void **)&thc->spec);
2347 thc->spec = cpystr(newhdr);
2348 if(ascii_colorstr(asciicolor, fghex) == 0) {
2349 if(thc->fg)
2350 fs_give((void **)&thc->fg);
2352 thc->fg = cpystr(asciicolor);
2354 else{
2355 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
2356 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2357 return(TCL_ERROR);
2360 if(ascii_colorstr(asciicolor, bghex) == 0) {
2361 if(thc->bg)
2362 fs_give((void **)&thc->bg);
2364 thc->bg = cpystr(asciicolor);
2366 else{
2367 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2368 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2369 return(TCL_ERROR);
2372 if(thc->val)
2373 fs_give((void **)&thc->val);
2375 if(newpat && *newpat){
2376 thc->val = string_to_pattern(newpat);
2379 else{
2380 Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
2381 return(TCL_ERROR);
2384 vtmp = &wps_global->vars[V_VIEW_HDR_COLORS];
2385 for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
2386 fs_give((void **)&vtmp->changed_val.l[i]);
2388 if(vtmp->changed_val.l)
2389 fs_give((void **)&vtmp->changed_val.l);
2391 vtmp->changed_val.l = varlist_from_spec_colors(hcolors);
2392 vtmp->is_changed_val = 1;
2393 free_spec_colors(&hcolors);
2394 return(TCL_OK);
2396 else {
2397 if(objc != 4){
2398 Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
2399 return(TCL_ERROR);
2402 if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
2403 && cObjc == 2
2404 && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
2405 && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
2406 Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
2407 return (TCL_ERROR);
2410 snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
2411 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
2412 vtmp->name && strucmp(vtmp->name, tvname);
2413 vtmp++)
2416 if(!vtmp->name || vtmp->is_list){
2417 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2418 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2419 return(TCL_ERROR);
2422 if(ascii_colorstr(asciicolor, fghex) == 0) {
2423 if(vtmp->changed_val.p)
2424 fs_give((void **)&vtmp->changed_val.p);
2426 vtmp->changed_val.p = cpystr(asciicolor);
2427 vtmp->is_changed_val = 1;
2429 /* We need to handle this in the actual config setting
2430 * if(!strucmp(varname, "normal"))
2431 * pico_set_fg_color(asciicolor);
2434 else{
2435 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
2436 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2437 return(TCL_ERROR);
2440 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
2441 vtmp++;
2442 if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
2443 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
2444 vtmp->name && strucmp(vtmp->name, tvname);
2445 vtmp++)
2448 if(!vtmp->name || vtmp->is_list){
2449 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
2450 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2451 return(TCL_ERROR);
2454 if(ascii_colorstr(asciicolor, bghex) == 0) {
2455 if(vtmp->changed_val.p)
2456 fs_give((void **)&vtmp->changed_val.p);
2458 vtmp->changed_val.p = cpystr(asciicolor);
2459 vtmp->is_changed_val = 1;
2460 /* again, we need to handle this when we actually set the variable
2461 * if(!strucmp(varname, "normal"))
2462 * pico_set_bg_color(asciicolor);
2465 else{
2466 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
2467 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
2468 return(TCL_ERROR);
2471 Tcl_SetResult(interp, "1", TCL_STATIC);
2472 return(TCL_OK);
2475 else if(!strcmp(s1, "ruleset")){
2476 return(peRuleSet(interp, &((Tcl_Obj **)objv)[2]));
2478 else if(objc == 2){
2479 if(!strcmp(s1, "featuresettings")){
2480 struct variable *vtmp;
2481 int i;
2482 FEATURE_S *feature;
2484 vtmp = &wps_global->vars[V_FEATURE_LIST];
2485 for(i = 0; (feature = feature_list(i)); i++)
2486 if(feature_list_section(feature)){
2487 if(vtmp->is_changed_val ? F_CH_ON(feature->id)
2488 : F_ON(feature->id, wps_global)){
2489 Tcl_ListObjAppendElement(interp,
2490 Tcl_GetObjResult(interp),
2491 Tcl_NewStringObj(feature->name, -1));
2494 return(TCL_OK);
2496 else if(!strcmp(s1, "rawsig")){
2497 char *err = NULL, *sig = NULL, *p, *q;
2498 int i;
2499 struct variable *vtmp;
2501 vtmp = &wps_global->vars[V_LITERAL_SIG];
2502 if(vtmp->is_changed_val ? vtmp->changed_val.p
2503 : wps_global->VAR_LITERAL_SIG){
2504 char *err = NULL;
2505 char **apval;
2507 if(wps_global->restricted){
2508 err = "Alpine demo can't change config file";
2510 else{
2511 /* BUG: no "exceptions file" support */
2512 apval = (vtmp->is_changed_val ? &vtmp->changed_val.p
2513 : APVAL(&wps_global->vars[V_LITERAL_SIG], Main));
2514 if(apval){
2515 sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
2516 sig[0] = '\0';
2517 cstring_to_string(*apval, sig);
2519 else
2520 err = "Problem accessing configuration";
2523 else if((vtmp = &wps_global->vars[V_SIGNATURE_FILE])
2524 && !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p
2525 : wps_global->VAR_SIGNATURE_FILE))
2526 snprintf(err = wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
2527 vtmp->is_changed_val ? (vtmp->changed_val.p
2528 ? vtmp->changed_val.p : "<null>")
2529 : (wps_global->VAR_SIGNATURE_FILE
2530 ? wps_global->VAR_SIGNATURE_FILE : "<null>"));
2531 else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val
2532 ? vtmp->changed_val.p
2533 : wps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))))
2534 err = "Can't read remote pinerc";
2536 if(err){
2537 Tcl_SetResult(interp, err, TCL_VOLATILE);
2538 return(TCL_ERROR);
2541 if(peTSig){
2542 for(i = 0; peTSig[i]; i++)
2543 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2544 Tcl_NewStringObj(peTSig[i],-1));
2546 else {
2547 for(p = sig; (q = strindex(p, '\n')); p = q + 1)
2548 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2549 Tcl_NewStringObj(p, q - p));
2551 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2552 Tcl_NewStringObj(p, -1));
2553 fs_give((void **) &sig);
2555 return(TCL_OK);
2557 else if(!strcmp(s1, "filters")){
2558 long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
2559 PAT_STATE pstate;
2560 PAT_S *pat;
2562 close_every_pattern();
2563 if(any_patterns(rflags, &pstate)){
2564 for(pat = first_pattern(&pstate);
2565 pat;
2566 pat = next_pattern(&pstate)){
2567 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2568 Tcl_NewStringObj(pat->patgrp->nick, -1));
2571 return(TCL_OK);
2573 else if(!strcmp(s1, "scores")){
2574 long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
2575 PAT_STATE pstate;
2576 PAT_S *pat;
2578 close_every_pattern();
2579 if(any_patterns(rflags, &pstate)){
2580 for(pat = first_pattern(&pstate);
2581 pat;
2582 pat = next_pattern(&pstate)){
2583 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2584 Tcl_NewStringObj(pat->patgrp->nick, -1));
2587 return(TCL_OK);
2589 else if(!strcmp(s1, "indexcolors")){
2590 long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
2591 PAT_STATE pstate;
2592 PAT_S *pat;
2594 close_every_pattern();
2595 if(any_patterns(rflags, &pstate)){
2596 for(pat = first_pattern(&pstate);
2597 pat;
2598 pat = next_pattern(&pstate)){
2599 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2600 Tcl_NewStringObj(pat->patgrp->nick, -1));
2603 return(TCL_OK);
2605 else if(!strcmp(s1, "collections")){
2606 struct variable *vtmp;
2607 int i;
2608 CONTEXT_S *new_ctxt;
2610 vtmp = &wps_global->vars[V_FOLDER_SPEC];
2611 for(i = 0; (vtmp->is_changed_val
2612 ? vtmp->changed_val.l && vtmp->changed_val.l[i]
2613 : vtmp->current_val.l && vtmp->current_val.l[i]);
2614 i++){
2615 new_ctxt = new_context(vtmp->is_changed_val
2616 ? vtmp->changed_val.l[i]
2617 : vtmp->current_val.l[i], NULL);
2618 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
2619 new_ctxt->nickname
2620 ? new_ctxt->nickname
2621 : (new_ctxt->server
2622 ? new_ctxt->server
2623 : (new_ctxt->label
2624 ? new_ctxt->label
2625 : "Some Collection")),
2626 new_ctxt->label ? new_ctxt->label : "");
2627 free_context(&new_ctxt);
2629 vtmp = &wps_global->vars[V_NEWS_SPEC];
2630 for(i = 0; (vtmp->is_changed_val
2631 ? vtmp->changed_val.l && vtmp->changed_val.l[i]
2632 : vtmp->current_val.l && vtmp->current_val.l[i]);
2633 i++){
2634 new_ctxt = new_context(vtmp->is_changed_val
2635 ? vtmp->changed_val.l[i]
2636 : vtmp->current_val.l[i], NULL);
2637 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
2638 new_ctxt->nickname
2639 ? new_ctxt->nickname
2640 : (new_ctxt->server
2641 ? new_ctxt->server
2642 : (new_ctxt->label
2643 ? new_ctxt->label
2644 : "Some Collection")),
2645 new_ctxt->label ? new_ctxt->label : "");
2646 free_context(&new_ctxt);
2649 return(TCL_OK);
2651 else if(!strcmp(s1, "newconf")){
2652 struct variable *vtmp;
2653 int i;
2654 FEATURE_S *feature;
2656 for(vtmp = wps_global->vars; vtmp->name; vtmp++)
2657 vtmp->is_changed_val = 0;
2659 for(i = 0; (feature = feature_list(i)); i++)
2660 F_CH_SET(feature->id, F_ON(feature->id, wps_global));
2662 if(peTSig){
2663 for(i = 0; peTSig[i]; i++)
2664 fs_give((void **)&peTSig[i]);
2665 fs_give((void **)&peTSig);
2668 close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED);
2669 return(TCL_OK);
2671 else if(!strcmp(s1, "saveconf")){
2672 struct variable *vtmp;
2673 int i, did_change = 0, def_sort_rev;
2674 FEATURE_S *feature;
2676 if(wps_global->vars[V_FEATURE_LIST].is_changed_val){
2677 wps_global->vars[V_FEATURE_LIST].is_changed_val = 0;
2678 for(i = 0; (feature = feature_list(i)); i++)
2679 if(feature_list_section(feature)){
2680 if(F_CH_ON(feature->id) != F_ON(feature->id, wps_global)){
2681 did_change = 1;
2682 toggle_feature(wps_global,
2683 &wps_global->vars[V_FEATURE_LIST],
2684 feature, TRUE, Main);
2689 for(vtmp = wps_global->vars; vtmp->name; vtmp++){
2690 if(vtmp->is_changed_val
2691 && (vtmp - wps_global->vars != V_FEATURE_LIST)){
2692 if(vtmp->is_list){
2693 for(i = 0; vtmp->main_user_val.l
2694 && vtmp->main_user_val.l[i]; i++)
2695 fs_give((void **)&vtmp->main_user_val.l[i]);
2696 if(vtmp->main_user_val.l)
2697 fs_give((void **)&vtmp->main_user_val.l);
2698 vtmp->main_user_val.l = vtmp->changed_val.l;
2699 vtmp->changed_val.l = NULL;
2701 else {
2702 if(vtmp->main_user_val.p)
2703 fs_give((void **)&vtmp->main_user_val.p);
2704 vtmp->main_user_val.p = vtmp->changed_val.p;
2705 vtmp->changed_val.p = NULL;
2707 set_current_val(vtmp, FALSE, FALSE);
2708 vtmp->is_changed_val = 0;
2709 did_change = 1;
2710 switch (vtmp - wps_global->vars) {
2711 case V_USER_DOMAIN:
2712 init_hostname(wps_global);
2713 case V_FOLDER_SPEC:
2714 case V_NEWS_SPEC:
2715 free_contexts(&wps_global->context_list);
2716 init_folders(wps_global);
2717 break;
2718 case V_NORM_FORE_COLOR:
2719 pico_set_fg_color(vtmp->current_val.p);
2720 break;
2721 case V_NORM_BACK_COLOR:
2722 pico_set_bg_color(vtmp->current_val.p);
2723 break;
2724 case V_ADDRESSBOOK:
2725 case V_GLOB_ADDRBOOK:
2726 #ifdef ENABLE_LDAP
2727 case V_LDAP_SERVERS:
2728 #endif
2729 case V_ABOOK_FORMATS:
2730 addrbook_reset();
2731 case V_INDEX_FORMAT:
2732 init_index_format(wps_global->VAR_INDEX_FORMAT,
2733 &wps_global->index_disp_format);
2734 clear_index_cache(sp_inbox_stream(), 0);
2735 break;
2736 case V_PAT_FILTS:
2737 close_patterns(ROLE_DO_FILTER | PAT_USE_CURRENT);
2738 role_process_filters();
2739 break;
2740 case V_PAT_INCOLS:
2741 close_patterns(ROLE_DO_INCOLS | PAT_USE_CURRENT);
2742 clear_index_cache(sp_inbox_stream(), 0);
2743 role_process_filters();
2744 break;
2745 case V_PAT_SCORES:
2746 close_patterns(ROLE_DO_SCORES | PAT_USE_CURRENT);
2747 role_process_filters();
2748 break;
2749 case V_DEFAULT_FCC:
2750 case V_DEFAULT_SAVE_FOLDER:
2751 init_save_defaults();
2752 break;
2753 case V_SORT_KEY:
2754 decode_sort(wps_global->VAR_SORT_KEY, &wps_global->def_sort, &def_sort_rev);
2755 break;
2756 case V_VIEW_HDR_COLORS :
2757 set_custom_spec_colors(wps_global);
2758 break;
2759 case V_POST_CHAR_SET :
2760 update_posting_charset(wps_global, 1);
2761 break;
2762 default:
2763 break;
2767 if(peTSig){
2768 peWriteSig(interp, wps_global->VAR_SIGNATURE_FILE, NULL);
2770 if(did_change){
2771 if(write_pinerc(wps_global, Main, WRP_NOUSER) == 0)
2772 q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!");
2774 return(TCL_OK);
2776 else if(!strcmp(s1, "columns")){
2777 Tcl_SetResult(interp, int2string(wps_global->ttyo->screen_cols), TCL_VOLATILE);
2778 return(TCL_OK);
2780 else if(!strcmp(s1, "indextokens")){
2781 INDEX_PARSE_T *tok;
2782 int i;
2784 for(i = 0; (tok = itoken(i)) != NULL; i++)
2785 if(tok->what_for & FOR_INDEX)
2786 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2787 Tcl_NewStringObj(tok->name, -1));
2789 return(TCL_OK);
2792 else if(objc == 3){
2793 if(!strcmp(s1, "varget")){
2794 char *varname = Tcl_GetStringFromObj(objv[2], NULL);
2795 struct variable *vtmp;
2796 Tcl_Obj *resObj, *secObj;
2797 char *input_type;
2798 int is_default, i;
2799 NAMEVAL_S *tmpnv;
2801 if(varname == NULL) return(TCL_ERROR);
2803 for(vtmp = wps_global->vars;
2804 vtmp->name && strucmp(vtmp->name, varname);
2805 vtmp++)
2808 if(!vtmp->name){
2809 Tcl_SetResult(interp, err, TCL_VOLATILE);
2810 return(TCL_ERROR);
2812 resObj = Tcl_NewListObj(0, NULL);
2813 if(vtmp->is_list){
2814 if(vtmp->is_changed_val){
2815 for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++){
2816 Tcl_ListObjAppendElement(interp, resObj,
2817 Tcl_NewStringObj(vtmp->changed_val.l[i], -1));
2820 else {
2821 for(i = 0; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
2822 Tcl_ListObjAppendElement(interp, resObj,
2823 Tcl_NewStringObj(vtmp->current_val.l[i], -1));
2827 else {
2828 if(vtmp->is_changed_val){
2829 if(vtmp->changed_val.p)
2830 Tcl_ListObjAppendElement(interp, resObj,
2831 Tcl_NewStringObj(vtmp->changed_val.p[0]
2832 ? vtmp->changed_val.p
2833 : "\"\"", -1));
2835 else {
2836 if(vtmp->current_val.p)
2837 Tcl_ListObjAppendElement(interp, resObj,
2838 Tcl_NewStringObj(vtmp->current_val.p[0]
2839 ? vtmp->current_val.p
2840 : "\"\"", -1));
2843 Tcl_ListObjAppendElement(interp,
2844 Tcl_GetObjResult(interp),
2845 resObj);
2846 secObj = Tcl_NewListObj(0, NULL);
2847 if(vtmp->is_list)
2848 input_type = cpystr("textarea");
2849 else{
2850 NAMEVAL_S *(*tmpf)(int);
2851 switch(vtmp - wps_global->vars){
2852 case V_SAVED_MSG_NAME_RULE:
2853 tmpf = save_msg_rules;
2854 break;
2855 case V_FCC_RULE:
2856 tmpf = fcc_rules;
2857 break;
2858 case V_SORT_KEY:
2859 tmpf = sort_key_rules;
2860 break;
2861 case V_AB_SORT_RULE:
2862 tmpf = ab_sort_rules;
2863 break;
2864 case V_FLD_SORT_RULE:
2865 tmpf = fld_sort_rules;
2866 break;
2867 case V_GOTO_DEFAULT_RULE:
2868 tmpf = goto_rules;
2869 break;
2870 case V_INCOMING_STARTUP:
2871 tmpf = incoming_startup_rules;
2872 break;
2873 case V_PRUNING_RULE:
2874 tmpf = pruning_rules;
2875 break;
2876 case V_WP_INDEXHEIGHT:
2877 tmpf = wp_indexheight_rules;
2878 break;
2879 default:
2880 tmpf = NULL;
2881 break;
2883 if(tmpf){
2884 for(i = 0; (tmpnv = (tmpf)(i)); i++){
2885 if(tmpnv->shortname)
2886 peAppListF(interp, secObj, "%s%s", tmpnv->name, tmpnv->shortname);
2887 else
2888 Tcl_ListObjAppendElement(interp, secObj,
2889 Tcl_NewStringObj(tmpnv->name, -1));
2891 input_type = cpystr("listbox");
2893 else
2894 input_type = cpystr("text");
2896 Tcl_ListObjAppendElement(interp,
2897 Tcl_GetObjResult(interp),
2898 Tcl_NewStringObj(input_type, -1));
2899 Tcl_ListObjAppendElement(interp,
2900 Tcl_GetObjResult(interp),
2901 secObj);
2902 if(vtmp->is_list)
2903 is_default = !vtmp->is_changed_val && !vtmp->main_user_val.l;
2904 else
2905 is_default = !vtmp->is_changed_val && !vtmp->main_user_val.p;
2906 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2907 Tcl_NewIntObj(is_default));
2908 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
2909 Tcl_NewIntObj(vtmp->is_fixed));
2910 return(TCL_OK);
2912 else if(!strcmp(s1, "filtextended")){
2913 int fl, i;
2914 long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
2915 PAT_STATE pstate;
2916 PAT_S *pat;
2917 Tcl_Obj *resObj = NULL, *tObj = NULL;
2919 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2920 return(TCL_ERROR);
2922 close_every_pattern();
2923 if(any_patterns(rflags, &pstate)){
2924 for(pat = first_pattern(&pstate), i = 0;
2925 pat && i != fl;
2926 pat = next_pattern(&pstate), i++);
2928 if(!pat)
2929 return(TCL_ERROR);
2931 /* append the pattern ID */
2932 tObj = Tcl_NewListObj(0, NULL);
2933 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
2934 pePatAppendID(interp, tObj, pat);
2935 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2937 /* append the pattern */
2938 tObj = Tcl_NewListObj(0, NULL);
2939 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
2940 pePatAppendPattern(interp, tObj, pat);
2941 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2943 /* now append the filter action */
2944 resObj = Tcl_NewListObj(0, NULL);
2945 peAppListF(interp, resObj, "%s%i", "kill", pat->action->folder ? 0 : 1);
2946 peAppListF(interp, resObj, "%s%p", "folder", pat->action->folder);
2947 peAppListF(interp, resObj, "%s%i", "move_only_if_not_deleted",
2948 pat->action->move_only_if_not_deleted);
2949 tObj = Tcl_NewListObj(0, NULL);
2950 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("filtaction", -1));
2951 Tcl_ListObjAppendElement(interp, tObj, resObj);
2952 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2954 else return(TCL_ERROR);
2956 return(TCL_OK);
2958 else if(!strcmp(s1, "indexcolorextended")){
2959 int fl, i;
2960 long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
2961 PAT_STATE pstate;
2962 PAT_S *pat;
2963 Tcl_Obj *resObj = NULL, *tObj = NULL;
2965 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
2966 return(TCL_ERROR);
2968 close_every_pattern();
2969 if(any_patterns(rflags, &pstate)){
2970 for(pat = first_pattern(&pstate), i = 0;
2971 pat && i != fl;
2972 pat = next_pattern(&pstate), i++);
2974 if(!pat)
2975 return(TCL_ERROR);
2977 /* append the pattern ID */
2978 tObj = Tcl_NewListObj(0, NULL);
2979 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
2980 pePatAppendID(interp, tObj, pat);
2981 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2983 /* append the pattern */
2984 tObj = Tcl_NewListObj(0, NULL);
2985 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
2986 pePatAppendPattern(interp, tObj, pat);
2987 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
2989 /* now append the pattern colors */
2990 resObj = Tcl_NewListObj(0, NULL);
2991 tObj = Tcl_NewListObj(0, NULL);
2992 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolor", -1));
2993 if(pat->action->is_a_incol){
2994 char *color;
2995 Tcl_Obj *colObj = Tcl_NewListObj(0, NULL);
2997 if(!(pat->action->incol
2998 && pat->action->incol->fg
2999 && pat->action->incol->fg[0]
3000 && (color = color_to_asciirgb(pat->action->incol->fg))
3001 && (color = peColorStr(color,wtmp_20k_buf))))
3002 color = "";
3004 Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
3006 if(!(pat->action->incol
3007 && pat->action->incol->bg
3008 && pat->action->incol->bg[0]
3009 && (color = color_to_asciirgb(pat->action->incol->bg))
3010 && (color = peColorStr(color,wtmp_20k_buf))))
3011 color = "";
3013 Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
3014 Tcl_ListObjAppendElement(interp, tObj, colObj);
3016 Tcl_ListObjAppendElement(interp, resObj, tObj);
3018 tObj = Tcl_NewListObj(0, NULL);
3019 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolors", -1));
3020 Tcl_ListObjAppendElement(interp, tObj, resObj);
3021 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3023 else return(TCL_ERROR);
3025 return(TCL_OK);
3027 else if(!strcmp(s1, "scoreextended")){
3028 int fl, i;
3029 long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
3030 char *hdr = NULL;
3031 PAT_STATE pstate;
3032 PAT_S *pat;
3033 Tcl_Obj *resObj = NULL, *tObj = NULL;
3035 if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
3036 return(TCL_ERROR);
3038 close_every_pattern();
3039 if(any_patterns(rflags, &pstate)){
3040 for(pat = first_pattern(&pstate), i = 0;
3041 pat && i != fl;
3042 pat = next_pattern(&pstate), i++);
3044 if(!pat)
3045 return(TCL_ERROR);
3047 /* append the pattern ID */
3048 tObj = Tcl_NewListObj(0, NULL);
3049 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
3050 pePatAppendID(interp, tObj, pat);
3051 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3053 /* append the pattern */
3054 tObj = Tcl_NewListObj(0, NULL);
3055 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
3056 pePatAppendPattern(interp, tObj, pat);
3057 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3059 /* now append the filter action */
3060 resObj = Tcl_NewListObj(0, NULL);
3061 peAppListF(interp, resObj, "%s%l", "scoreval", pat->action->scoreval);
3062 if(pat->action->scorevalhdrtok)
3063 hdr = hdrtok_to_stringform(pat->action->scorevalhdrtok);
3065 peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : "");
3067 if(hdr)
3068 fs_give((void **) &hdr);
3070 tObj = Tcl_NewListObj(0, NULL);
3071 Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("scores", -1));
3072 Tcl_ListObjAppendElement(interp, tObj, resObj);
3073 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
3075 else return(TCL_ERROR);
3077 return(TCL_OK);
3079 else if(!strcmp(s1, "clextended")){
3080 int cl, i, j = 0, in_folder_spec = 0;
3081 struct variable *vtmp;
3082 char tpath[MAILTMPLEN], *p;
3083 CONTEXT_S *ctxt;
3085 vtmp = &wps_global->vars[V_FOLDER_SPEC];
3086 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3087 return(TCL_ERROR);
3088 for(i = 0; i < cl && (vtmp->is_changed_val
3089 ? (vtmp->changed_val.l
3090 && vtmp->changed_val.l[i])
3091 : (vtmp->current_val.l
3092 && vtmp->current_val.l[i])); i++);
3093 if(i == cl && (vtmp->is_changed_val
3094 ? vtmp->changed_val.l && vtmp->changed_val.l[i]
3095 : vtmp->current_val.l && vtmp->current_val.l[i]))
3096 in_folder_spec = 1;
3097 else {
3098 vtmp = &wps_global->vars[V_NEWS_SPEC];
3099 for(j = 0; i + j < cl && (vtmp->is_changed_val
3100 ? (vtmp->changed_val.l
3101 && vtmp->changed_val.l[j])
3102 : (vtmp->current_val.l
3103 && vtmp->current_val.l[j])); j++);
3105 if(in_folder_spec || (i + j == cl && (vtmp->is_changed_val
3106 ? vtmp->changed_val.l && vtmp->changed_val.l[j]
3107 : vtmp->current_val.l && vtmp->current_val.l[j]))){
3108 ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[in_folder_spec ? i : j]
3109 : vtmp->current_val.l[in_folder_spec ? i : j], NULL);
3110 Tcl_ListObjAppendElement(interp,
3111 Tcl_GetObjResult(interp),
3112 Tcl_NewStringObj(ctxt->nickname ? ctxt->nickname : "", -1));
3113 Tcl_ListObjAppendElement(interp,
3114 Tcl_GetObjResult(interp),
3115 Tcl_NewStringObj(ctxt->label ? ctxt->label : "", -1));
3116 Tcl_ListObjAppendElement(interp,
3117 Tcl_GetObjResult(interp),
3118 Tcl_NewStringObj(ctxt->server ? ctxt->server : "", -1));
3119 tpath[0] = '\0';
3120 if(ctxt->context){
3121 strncpy(tpath, (ctxt->context[0] == '{'
3122 && (p = strchr(ctxt->context, '}')))
3123 ? ++p
3124 : ctxt->context, sizeof(tpath));
3125 tpath[sizeof(tpath)-1] = '\0';
3126 if((p = strstr(tpath, "%s")) != NULL)
3127 *p = '\0';
3129 Tcl_ListObjAppendElement(interp,
3130 Tcl_GetObjResult(interp),
3131 Tcl_NewStringObj(tpath, -1));
3132 Tcl_ListObjAppendElement(interp,
3133 Tcl_GetObjResult(interp),
3134 Tcl_NewStringObj(ctxt->dir && ctxt->dir->view.user
3135 ? ctxt->dir->view.user : "", -1));
3136 free_context(&ctxt);
3138 return(TCL_OK);
3140 else
3141 return(TCL_ERROR);
3143 else if(!strcmp(s1, "rawsig")){
3144 struct variable *vtmp;
3145 char *cstring_version, *sig, *line;
3146 int i, nSig;
3147 Tcl_Obj **objSig;
3149 vtmp = &wps_global->vars[V_LITERAL_SIG];
3150 if(vtmp->is_changed_val ? vtmp->changed_val.p
3151 : wps_global->VAR_LITERAL_SIG){
3153 wtmp_20k_buf[0] = '\0';
3154 Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
3155 for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
3156 if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
3157 snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
3159 sig = cpystr(wtmp_20k_buf);
3161 if((cstring_version = string_to_cstring(sig)) != NULL){
3162 if(vtmp->changed_val.p)
3163 fs_give((void **)&vtmp->changed_val.p);
3164 vtmp->is_changed_val = 1;
3165 vtmp->changed_val.p = cstring_version;
3168 fs_give((void **) &sig);
3169 return(TCL_OK);
3171 else {
3172 if(peTSig){
3173 for(i = 0; peTSig[i]; i++)
3174 fs_give((void **)&peTSig[i]);
3175 fs_give((void **)&peTSig);
3177 Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
3178 peTSig = (char **)fs_get(sizeof(char)*(nSig + 1));
3179 for(i = 0; i < nSig; i++){
3180 line = Tcl_GetStringFromObj(objSig[i], NULL);
3181 peTSig[i] = cpystr(line ? line : "");
3183 peTSig[i] = NULL;
3184 return(TCL_OK);
3187 else if(!strcmp(s1, "colorget")){
3188 char *varname;
3189 char tvname[256], hexcolor[256];
3190 struct variable *vtmp;
3191 if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
3192 return(TCL_ERROR);
3194 if(strcmp("viewer-hdr-colors", varname) == 0){
3195 SPEC_COLOR_S *hcolors, *thc;
3196 Tcl_Obj *resObj;
3197 char hexcolor[256], *tstr = NULL;
3199 if(wps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
3200 hcolors = spec_colors_from_varlist(wps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
3201 else
3202 hcolors = spec_colors_from_varlist(wps_global->VAR_VIEW_HDR_COLORS, 0);
3203 for(thc = hcolors; thc; thc = thc->next){
3204 resObj = Tcl_NewListObj(0,NULL);
3205 Tcl_ListObjAppendElement(interp, resObj,
3206 Tcl_NewStringObj(thc->spec, -1));
3207 hex_colorstr(hexcolor, thc->fg);
3208 Tcl_ListObjAppendElement(interp, resObj,
3209 Tcl_NewStringObj(hexcolor, -1));
3210 hex_colorstr(hexcolor, thc->bg);
3211 Tcl_ListObjAppendElement(interp, resObj,
3212 Tcl_NewStringObj(hexcolor, -1));
3213 Tcl_ListObjAppendElement(interp, resObj,
3214 Tcl_NewStringObj(thc->val
3215 ? tstr = pattern_to_string(thc->val)
3216 : "", -1));
3217 if(tstr) fs_give((void **)&tstr);
3218 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3219 resObj);
3221 fs_give((void **)&hcolors);
3222 return(TCL_OK);
3224 else {
3225 char *colorp;
3227 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
3229 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
3230 vtmp->name && strucmp(vtmp->name, tvname);
3231 vtmp++)
3234 if(!vtmp->name) return(TCL_ERROR);
3235 if(vtmp->is_list) return(TCL_ERROR);
3237 colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
3238 ? vtmp->changed_val.p
3239 : (vtmp->current_val.p) ? vtmp->current_val.p
3240 : vtmp->global_val.p;
3242 if(colorp){
3243 hex_colorstr(hexcolor, colorp);
3244 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3245 Tcl_NewStringObj(hexcolor, -1));
3247 else
3248 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3249 Tcl_NewStringObj("", -1));
3251 snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
3252 vtmp++;
3253 if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
3254 for(vtmp = &wps_global->vars[V_NORM_FORE_COLOR];
3255 vtmp->name && strucmp(vtmp->name, tvname);
3256 vtmp++)
3259 if(!vtmp->name) return(TCL_ERROR);
3260 if(vtmp->is_list) return(TCL_ERROR);
3262 colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
3263 ? vtmp->changed_val.p
3264 : (vtmp->current_val.p) ? vtmp->current_val.p
3265 : vtmp->global_val.p;
3267 if(colorp){
3268 hex_colorstr(hexcolor, colorp);
3269 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3270 Tcl_NewStringObj(hexcolor, -1));
3272 else
3273 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
3274 Tcl_NewStringObj("", -1));
3276 return(TCL_OK);
3278 else if(!strcmp(s1, "cldel")){
3279 int cl, i, j, n;
3280 struct variable *vtmp;
3281 char **newl;
3283 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3284 return(TCL_ERROR);
3285 vtmp = &wps_global->vars[V_FOLDER_SPEC];
3286 for(i = 0; i < cl && (vtmp->is_changed_val
3287 ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3288 : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
3289 if(!(i == cl && (vtmp->is_changed_val
3290 ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3291 : (vtmp->current_val.l && vtmp->current_val.l[i])))){
3292 vtmp = &wps_global->vars[V_NEWS_SPEC];
3293 for(j = 0; i + j < cl && (vtmp->is_changed_val
3294 ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3295 : (vtmp->current_val.l && vtmp->current_val.l[j]));
3296 j++);
3297 if(!(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 return(TCL_ERROR);
3301 i = j;
3303 for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
3304 : (vtmp->current_val.l && vtmp->current_val.l[n]); n++);
3305 newl = (char **)fs_get(n*(sizeof(char *)));
3306 for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
3307 : (vtmp->current_val.l && vtmp->current_val.l[n]); n++){
3308 if(n < i)
3309 newl[n] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
3310 : vtmp->current_val.l[n]);
3311 else if(n > i)
3312 newl[n-1] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
3313 : vtmp->current_val.l[n]);
3315 newl[n-1] = NULL;
3316 vtmp->is_changed_val = 1;
3317 for(n = 0; vtmp->changed_val.l && vtmp->changed_val.l[n]; n++)
3318 fs_give((void **) &vtmp->changed_val.l[n]);
3319 if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3320 vtmp->changed_val.l = newl;
3322 return(TCL_OK);
3324 else if(!strcmp(s1, "columns")){
3325 int n;
3326 char *p;
3328 if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR
3329 && n >= MIN_SCREEN_COLS
3330 && n < (MAX_SCREEN_COLS - 1)
3331 && wps_global->ttyo->screen_cols != n){
3332 clear_index_cache(sp_inbox_stream(), 0);
3333 wps_global->ttyo->screen_cols = n;
3334 set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main);
3335 Tcl_SetResult(interp, p, TCL_VOLATILE);
3337 else
3338 Tcl_SetResult(interp, int2string(wps_global->ttyo->screen_cols), TCL_VOLATILE);
3340 return(TCL_OK);
3342 else if(!strcmp(s1, "reset")){
3343 char *p;
3345 if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
3346 if(!strcmp(p,"pinerc")){
3347 struct variable *var;
3348 PINERC_S *prc;
3350 /* new pinerc structure, copy location pointers */
3351 prc = new_pinerc_s(wps_global->prc->name);
3352 prc->type = wps_global->prc->type;
3353 prc->rd = wps_global->prc->rd;
3354 prc->outstanding_pinerc_changes = 1;
3356 /* tie off original pinerc struct and free it */
3357 wps_global->prc->rd = NULL;
3358 wps_global->prc->outstanding_pinerc_changes = 0;
3359 free_pinerc_s(&wps_global->prc);
3361 /* set global->prc to new struct with no pinerc_lines
3362 * and fool write_pinerc into not writing changed vars
3364 wps_global->prc = prc;
3367 * write at least one var into nearly empty pinerc
3368 * and clear user's var settings. clear global cause
3369 * they'll get reset in peInitVars
3371 for(var = wps_global->vars; var->name != NULL; var++){
3372 var->been_written = ((var - wps_global->vars) != V_LAST_VERS_USED);
3373 if(var->is_list){
3374 free_list_array(&var->main_user_val.l);
3375 free_list_array(&var->global_val.l);
3377 else{
3378 fs_give((void **)&var->main_user_val.p);
3379 fs_give((void **)&var->global_val.p);
3383 write_pinerc(wps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN);
3385 peInitVars(wps_global);
3386 return(TCL_OK);
3391 else if(objc == 4){
3392 if(!strcmp(s1, "varset")){
3393 char *varname = Tcl_GetStringFromObj(objv[2], NULL);
3394 struct variable *vtmp;
3395 char **tstrlist = NULL, *line, *tline;
3396 Tcl_Obj **objVal;
3397 int i, strlistpos, numlistvals;
3399 if(varname == NULL) return(TCL_ERROR);
3400 for(vtmp = wps_global->vars;
3401 vtmp->name && strucmp(vtmp->name, varname);
3402 vtmp++)
3404 if(!vtmp->name){
3405 Tcl_SetResult(interp, err, TCL_VOLATILE);
3406 return(TCL_ERROR);
3408 if(Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
3409 &objVal) != TCL_OK)
3410 return(TCL_ERROR);
3411 vtmp->is_changed_val = 1;
3412 if(vtmp->is_list){
3413 if(vtmp->changed_val.l){
3414 for(i = 0; vtmp->changed_val.l[i]; i++)
3415 fs_give((void **)&vtmp->changed_val.l[i]);
3416 fs_give((void **)&vtmp->changed_val.l);
3418 if(numlistvals)
3419 tstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
3420 for(i = 0, strlistpos = 0; i < numlistvals; i++){
3421 if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
3422 tline = cpystr(line);
3423 removing_leading_and_trailing_white_space(tline);
3424 if(*tline)
3425 tstrlist[strlistpos++] = cpystr(tline);
3426 fs_give((void **) &tline);
3429 if(tstrlist)
3430 tstrlist[strlistpos] = NULL;
3431 vtmp->changed_val.l = tstrlist;
3433 else {
3434 if(vtmp->changed_val.p)
3435 fs_give((void **)&vtmp->changed_val.p);
3436 if(numlistvals){
3437 if((line = Tcl_GetStringFromObj(objVal[0], 0)) != NULL){
3438 tline = cpystr(line);
3439 if(strucmp(vtmp->name, "reply-indent-string"))
3440 removing_leading_and_trailing_white_space(tline);
3441 if(!strcmp(tline, "\"\"")){
3442 tline[0] = '\0';
3444 else if(tline[0] == '\0'){
3445 fs_give((void **)&tline);
3447 if(tline){
3448 vtmp->changed_val.p = cpystr(tline);
3449 fs_give((void **)&tline);
3452 else
3453 vtmp->changed_val.p = cpystr("");
3456 return(TCL_OK);
3458 else if(!strcmp(s1, "feature")){
3459 char *featurename;
3460 int i, set, wasset = 0;
3461 FEATURE_S *feature;
3464 * CMD: feature
3466 * ARGS: featurename -
3467 * value - new value to assign flag
3469 * Returns: 1 if named feature set, 0 otherwise
3472 if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
3473 && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
3474 for(i = 0; (feature = feature_list(i)); i++)
3475 if(!strucmp(featurename, feature->name)){
3476 wps_global->vars[V_FEATURE_LIST].is_changed_val = 1;
3477 wasset = F_CH_ON(feature->id);
3478 F_CH_SET(feature->id, set);
3479 break;
3482 Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
3483 return(TCL_OK);
3485 else if(!strcmp(s1, "clshuff")){
3486 char *dir, *tstr, **newl;
3487 int cl, up = 0, fvarn, nvarn, icnt, i;
3488 struct variable *fvar, *nvar, *vtmp;
3490 if(!(dir = Tcl_GetStringFromObj(objv[2], NULL)))
3491 return TCL_ERROR;
3492 if(Tcl_GetIntFromObj(interp, objv[3], &cl) == TCL_ERROR)
3493 return(TCL_ERROR);
3494 if(!strcmp(dir, "up"))
3495 up = 1;
3496 else if(!strcmp(dir, "down"))
3497 up = 0;
3498 else
3499 return(TCL_ERROR);
3500 fvar = &wps_global->vars[V_FOLDER_SPEC];
3501 nvar = &wps_global->vars[V_NEWS_SPEC];
3502 for(fvarn = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[fvarn])
3503 : (fvar->current_val.l && fvar->current_val.l[fvarn]); fvarn++);
3504 for(nvarn = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[nvarn])
3505 : (nvar->current_val.l && nvar->current_val.l[nvarn]); nvarn++);
3506 if(cl < fvarn){
3507 vtmp = fvar;
3508 icnt = cl;
3510 else if(cl >= fvarn && cl < nvarn + fvarn){
3511 vtmp = nvar;
3512 icnt = cl - fvarn;
3514 else
3515 return(TCL_ERROR);
3516 if(vtmp == nvar && icnt == 0 && up){
3517 newl = (char **)fs_get((fvarn + 2)*sizeof(char *));
3518 for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i])
3519 : (fvar->current_val.l && fvar->current_val.l[i]); i++)
3520 newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3521 : fvar->current_val.l[i]);
3522 newl[i++] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[0]
3523 : nvar->current_val.l[0]);
3524 newl[i] = NULL;
3525 fvar->is_changed_val = 1;
3526 for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
3527 fs_give((void **)&fvar->changed_val.l[i]);
3528 if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
3529 fvar->changed_val.l = newl;
3530 newl = (char **)fs_get(nvarn*sizeof(char *));
3531 for(i = 1; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
3532 : (nvar->current_val.l && nvar->current_val.l[i]); i++)
3533 newl[i-1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
3534 : nvar->current_val.l[i]);
3535 newl[i-1] = NULL;
3536 nvar->is_changed_val = 1;
3537 for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
3538 fs_give((void **)&nvar->changed_val.l[i]);
3539 if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
3540 nvar->changed_val.l = newl;
3541 vtmp = fvar;
3542 icnt = fvarn;
3544 else if(vtmp == fvar && icnt == fvarn - 1 && !up){
3545 newl = (char **)fs_get(fvarn*sizeof(char *));
3546 for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i+1])
3547 : (fvar->current_val.l && fvar->current_val.l[i+1]); i++)
3548 newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3549 : fvar->current_val.l[i]);
3550 newl[i] = NULL;
3551 tstr = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
3552 : fvar->current_val.l[i]);
3553 fvar->is_changed_val = 1;
3554 for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
3555 fs_give((void **)&fvar->changed_val.l[i]);
3556 if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
3557 fvar->changed_val.l = newl;
3558 newl = (char **)fs_get((nvarn+2)*sizeof(char *));
3559 newl[0] = tstr;
3560 for(i = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
3561 : (nvar->current_val.l && nvar->current_val.l[i]); i++)
3562 newl[i+1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
3563 : nvar->current_val.l[i]);
3564 newl[i+1] = NULL;
3565 nvar->is_changed_val = 1;
3566 for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
3567 fs_give((void **)&nvar->changed_val.l[i]);
3568 if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
3569 nvar->changed_val.l = newl;
3570 vtmp = nvar;
3571 icnt = 0;
3573 else {
3574 newl = (char **)fs_get(((vtmp == fvar ? fvarn : nvarn) + 1)*sizeof(char *));
3575 for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3576 : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
3577 newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
3578 : vtmp->current_val.l[i]);
3579 newl[i] = NULL;
3580 vtmp->is_changed_val = 1;
3581 for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
3582 fs_give((void **)&vtmp->changed_val.l[i]);
3583 if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3584 vtmp->changed_val.l = newl;
3586 if(up){
3587 tstr = vtmp->changed_val.l[icnt-1];
3588 vtmp->changed_val.l[icnt-1] = vtmp->changed_val.l[icnt];
3589 vtmp->changed_val.l[icnt] = tstr;
3591 else {
3592 tstr = vtmp->changed_val.l[icnt+1];
3593 vtmp->changed_val.l[icnt+1] = vtmp->changed_val.l[icnt];
3594 vtmp->changed_val.l[icnt] = tstr;
3596 return(TCL_OK);
3599 else if(objc == 7){
3600 if(!strcmp(s1, "cledit") || !strcmp(s1, "cladd")){
3601 int add = 0, cl, quotes_needed = 0, i, j, newn;
3602 char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4];
3603 char **newl;
3604 struct variable *vtmp;
3606 if(!strcmp(s1, "cladd")) add = 1;
3608 if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
3609 return(TCL_ERROR);
3610 if(!(nick = Tcl_GetStringFromObj(objv[3], NULL)))
3611 return TCL_ERROR;
3612 if(!(server = Tcl_GetStringFromObj(objv[4], NULL)))
3613 return TCL_ERROR;
3614 if(!(path = Tcl_GetStringFromObj(objv[5], NULL)))
3615 return TCL_ERROR;
3616 if(!(view = Tcl_GetStringFromObj(objv[6], NULL)))
3617 return TCL_ERROR;
3618 removing_leading_and_trailing_white_space(nick);
3619 removing_leading_and_trailing_white_space(server);
3620 removing_leading_and_trailing_white_space(path);
3621 removing_leading_and_trailing_white_space(view);
3622 if(strchr(nick, ' '))
3623 quotes_needed = 1;
3624 if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
3625 MAILTMPLEN * 4 - 20) { /* for good measure */
3626 Tcl_SetResult(interp, "info too long", TCL_VOLATILE);
3627 return TCL_ERROR;
3629 if(3 + strlen(nick) + strlen(server) + strlen(path) +
3630 strlen(view) > MAILTMPLEN + 4){
3631 Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE);
3632 return(TCL_OK);
3634 snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
3635 "\"" : "", nick, quotes_needed ? "\"" : "",
3636 strlen(nick) ? " " : "",
3637 server, path, view);
3638 if(add) {
3639 vtmp = &wps_global->vars[V_NEWS_SPEC];
3640 if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[0])
3641 : (vtmp->current_val.l && vtmp->current_val.l[0])))
3642 vtmp = &wps_global->vars[V_FOLDER_SPEC];
3643 for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3644 : (vtmp->current_val.l && vtmp->current_val.l[i]); i++);
3645 newn = i + 1;
3646 newl = (char **)fs_get((newn + 1)*sizeof(char *));
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 newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
3650 : vtmp->current_val.l[i]);
3651 newl[i++] = cpystr(context_buf);
3652 newl[i] = NULL;
3654 else {
3655 vtmp = &wps_global->vars[V_FOLDER_SPEC];
3656 for(i = 0; i < cl && (vtmp->is_changed_val
3657 ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3658 : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
3659 if(!(i == cl && (vtmp->is_changed_val
3660 ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
3661 : (vtmp->current_val.l && vtmp->current_val.l[i])))){
3662 vtmp = &wps_global->vars[V_NEWS_SPEC];
3663 for(j = 0; i + j < cl && (vtmp->is_changed_val
3664 ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3665 : (vtmp->current_val.l && vtmp->current_val.l[j]));
3666 j++);
3667 if(!(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 return(TCL_ERROR);
3671 i = j;
3673 for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3674 : (vtmp->current_val.l && vtmp->current_val.l[j]); j++);
3675 newl = (char **)fs_get(j * sizeof(char *));
3676 for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
3677 : (vtmp->current_val.l && vtmp->current_val.l[j]); j++){
3678 if(j == i)
3679 newl[j] = cpystr(context_buf);
3680 else
3681 newl[j] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[j]
3682 : vtmp->current_val.l[j]);
3684 newl[j] = NULL;
3686 vtmp->is_changed_val = 1;
3687 for(j = 0; vtmp->changed_val.l && vtmp->changed_val.l[j]; j++)
3688 fs_give((void **)&vtmp->changed_val.l[j]);
3689 if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
3690 vtmp->changed_val.l = newl;
3691 return TCL_OK;
3694 else
3695 err = "PEInfo: Too many arguments";
3697 Tcl_SetResult(interp, err, TCL_STATIC);
3698 return(TCL_ERROR);
3703 peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv)
3705 int try_cache, e, i, n, nSig;
3706 char datebuf[200], *sig, *line;
3707 FILE *fp;
3708 REMDATA_S *rd;
3709 Tcl_Obj **objSig;
3711 if(!(file && IS_REMOTE(file))){
3712 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
3713 file ? file : "<null>");
3714 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3715 return(TCL_ERROR);
3719 * We could parse the name here to find what type it is. So far we
3720 * only have type RemImap.
3722 rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE,
3723 NULL, "Error: ", "Can't fetch remote signature.");
3724 if(!rd){
3725 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file);
3726 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3727 return(TCL_ERROR);
3730 try_cache = rd_read_metadata(rd);
3732 if(rd->access == MaybeRorW){
3733 if(rd->read_status == 'R')
3734 rd->access = ReadOnly;
3735 else
3736 rd->access = ReadWrite;
3739 if(rd->access != NoExists){
3741 rd_check_remvalid(rd, 1L);
3744 * If the cached info says it is readonly but
3745 * it looks like it's been fixed now, change it to readwrite.
3747 if(rd->read_status == 'R'){
3749 * We go to this trouble since readonly sigfiles
3750 * are likely a mistake. They are usually supposed to be
3751 * readwrite so we open it and check if it's been fixed.
3753 rd_check_readonly_access(rd);
3754 if(rd->read_status == 'W'){
3755 rd->access = ReadWrite;
3756 rd->flags |= REM_OUTOFDATE;
3758 else{
3759 rd_close_remdata(&rd);
3760 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file);
3761 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3762 return(TCL_ERROR);
3766 if(rd->flags & REM_OUTOFDATE){
3767 if(rd_update_local(rd) != 0){
3769 dprint((1, "pinerc_remote_open: rd_update_local failed"));
3771 * Don't give up altogether. We still may be
3772 * able to use a cached copy.
3775 else{
3776 dprint((7, "%s: copied remote to local (%ld)",
3777 rd->rn, (long)rd->last_use));
3781 if(rd->access == ReadWrite)
3782 rd->flags |= DO_REMTRIM;
3785 /* If we couldn't get to remote folder, try using the cached copy */
3786 if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
3787 rd_close_remdata(&rd);
3788 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file);
3789 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3790 return(TCL_ERROR);
3793 unlink(rd->lf);
3795 sig = NULL;
3796 wtmp_20k_buf[0] = '\0';
3797 if(objv){
3798 Tcl_ListObjGetElements(interp, objv[0], &nSig, &objSig);
3799 for(i = 0; i < nSig && i < SIG_MAX_LINES; i++){
3800 if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
3801 snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n",
3802 SIG_MAX_COLS, line);
3805 else if(peTSig){
3806 for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) {
3807 snprintf(wtmp_20k_buf + strlen(wtmp_20k_buf), SIZEOF_20KBUF - strlen(wtmp_20k_buf), "%.*s\n",
3808 SIG_MAX_COLS, peTSig[i]);
3810 for(i = 0; peTSig[i]; i++)
3811 fs_give((void **)&peTSig[i]);
3812 fs_give((void **)&peTSig);
3814 else
3815 return(TCL_ERROR);
3817 sig = cpystr(wtmp_20k_buf);
3819 if((fp = fopen(rd->lf, "w")) != NULL)
3820 n = fwrite(sig, strlen(sig), 1, fp);
3822 fs_give((void **) &sig);
3824 if(fp){
3825 if(n != 1){
3826 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s",
3827 rd->lf, error_description(errno));
3828 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3829 rd_close_remdata(&rd);
3832 fclose(fp);
3833 if(n != 1)
3834 return(TCL_ERROR);
3836 else {
3837 rd_close_remdata(&rd);
3838 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s",
3839 rd->lf, error_description(errno));
3840 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3841 return(TCL_ERROR);
3844 datebuf[0] = '\0';
3846 if(!rd->t.i.stream){
3847 long retflags = 0;
3849 rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags);
3852 if((e = rd_update_remote(rd, datebuf)) != 0){
3853 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s",
3854 rd->lf, error_description(errno));
3855 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
3856 rd_close_remdata(&rd);
3857 return(TCL_ERROR);
3860 rd_update_metadata(rd, datebuf);
3861 rd->read_status = 'W';
3862 rd_close_remdata(&rd);
3863 return(TCL_OK);
3868 NAMEVAL_S *sort_key_rules(int index)
3870 static NAMEVAL_S is_rules[] = {
3871 {"Arrival", 0},
3872 {"Date", 0},
3873 {"Subject", 0},
3874 {"Cc", 0},
3875 {"From", 0},
3876 {"To", 0},
3877 {"size", 0},
3878 {"OrderedSubj", 0},
3879 {"tHread", 0},
3880 {"Arrival/Reverse", 0},
3881 {"Date/Reverse", 0},
3882 {"Subject/Reverse", 0},
3883 {"Cc/Reverse", 0},
3884 {"From/Reverse", 0},
3885 {"To/Reverse", 0},
3886 {"size/Reverse", 0},
3887 {"tHread/Reverse", 0},
3888 {"OrderedSubj/Reverse", 0}
3891 return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3892 ? &is_rules[index] : NULL);
3895 NAMEVAL_S *wp_indexheight_rules(int index)
3897 static NAMEVAL_S is_rules[] = {
3898 {"normal font", "24", 0},
3899 {"smallest font", "20", 0},
3900 {"small font", "22", 0},
3901 {"large font", "28", 0},
3902 {"largest font", "30", 0}
3905 return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
3906 ? &is_rules[index] : NULL);
3911 * PEDebugCmd - turn on/off and set various debugging options
3914 PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
3916 char *s;
3918 if(!--objc){ /* only one arg? */
3919 Tcl_WrongNumArgs(interp, 1, objv, "?args?");
3921 else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
3922 if(!strucmp(s, "level")){
3923 if(objc == 2){
3924 int level;
3926 if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3927 return(TCL_ERROR);
3929 if(level > 0){
3930 if(level > 10)
3931 level = 10;
3933 debug = level;
3934 dprint((1, "Debug level %d", level));
3936 else{
3937 dprint((1, "PEDebug ending"));
3938 debug = 0;
3942 Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE);
3943 return(TCL_OK);
3945 else if(!strucmp(s, "write")){
3946 if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){
3948 * script debugging has a high priority since
3949 * statements can be added/removed on the fly
3950 * AND are NOT present by default
3952 dprint((SYSDBG_INFO, "SCRIPT: %s", s));
3955 return(TCL_OK);
3957 else if(!strucmp(s, "imap")){
3958 int level;
3960 if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
3961 return(TCL_ERROR);
3963 if(level == 0){
3964 if(wps_global){
3965 wps_global->debug_imap = 0;
3966 if(wps_global->mail_stream)
3967 mail_nodebug(wps_global->mail_stream);
3970 else if(level > 0 && level < 5){
3971 if(wps_global){
3972 wps_global->debug_imap = level;
3973 if(wps_global->mail_stream)
3974 mail_debug(wps_global->mail_stream);
3978 return(TCL_OK);
3980 else
3981 Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC);
3984 return(TCL_ERROR);
3989 * PESessionCmd - Export TCL Session-wide command set
3992 PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
3994 char *op, *err = "Unknown PESession option";
3995 char *pe_user, *pe_host;
3996 int pe_alt, l;
3998 dprint((2, "PESessionCmd"));
4000 if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4001 if(!strcmp(op, "open")){
4002 char *s, *pinerc, *pineconf = NULL;
4005 * CMD: open user remote-pinerc local-default-config
4007 * Initiate a session
4009 * Returns: error string on error, nothing otherwise
4012 if(objc < 4 || objc > 5){
4013 Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc");
4014 return(TCL_ERROR);
4017 if(!(s = Tcl_GetStringFromObj(objv[2], &l))){
4018 Tcl_SetResult(interp, "Unknown User", TCL_STATIC);
4019 return(TCL_ERROR);
4021 else{
4022 int rv;
4024 pe_user = cpystr(s);
4026 #if defined(HAVE_SETENV)
4027 rv = setenv("WPUSER", pe_user, 1);
4028 #elif defined(HAVE_PUTENV)
4030 static char putenvbuf[PUTENV_MAX];
4032 if(l + 8 < PUTENV_MAX){
4033 if(putenvbuf[0]) /* only called once, but you never know */
4034 snprintf(putenvbuf + 7, PUTENV_MAX - 7, "%s", pe_user);
4035 else
4036 snprintf(putenvbuf, PUTENV_MAX, "WPUSER=%s", pe_user);
4038 rv = putenv(putenvbuf);
4040 else
4041 rv = 1;
4043 #endif
4045 if(rv){
4046 fs_give((void **) &pe_user);
4047 Tcl_SetResult(interp, (errno == ENOMEM)
4048 ? "Insufficient Environment Space"
4049 : "Cannot set WPUSER in environment", TCL_STATIC);
4050 return(TCL_ERROR);
4054 if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4055 NETMBX mb;
4057 if(mail_valid_net_parse(pinerc, &mb)){
4058 pe_host = cpystr(mb.host);
4059 pe_alt = (mb.sslflag || mb.tlsflag);
4061 else {
4062 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc);
4063 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4064 return(TCL_ERROR);
4067 else {
4068 Tcl_SetResult(interp, "Unknown config location", TCL_STATIC);
4069 return(TCL_ERROR);
4072 if(objc == 5 && !(pineconf = Tcl_GetStringFromObj(objv[4], NULL))){
4073 Tcl_SetResult(interp, "Can't determine global config", TCL_STATIC);
4074 return(TCL_ERROR);
4077 dprint((SYSDBG_INFO, "session (%s) %s - %s",
4078 pe_user, pinerc, pineconf ? pineconf : "<none>"));
4080 /* credential cache MUST already be seeded */
4082 /* destroy old user context */
4083 if(wps_global){
4084 /* destroy open stream */
4085 peDestroyStream(wps_global);
4087 /* destroy old user context */
4088 peDestroyUserContext(&wps_global);
4091 /* Establish a user context */
4092 if((s = peCreateUserContext(interp, pe_user, pinerc, pineconf)) != NULL){
4093 Tcl_SetResult(interp, s, TCL_VOLATILE);
4094 return(TCL_ERROR);
4097 fs_give((void **) &pe_user);
4098 fs_give((void **) &pe_host);
4100 return(TCL_OK);
4102 else if(!strcmp(op, "close")){
4103 if(wps_global){
4104 /* destroy any open stream */
4105 peDestroyStream(wps_global);
4107 /* destroy user context */
4108 peDestroyUserContext(&wps_global);
4111 Tcl_SetResult(interp, "BYE", TCL_STATIC);
4112 return(TCL_OK);
4114 else if(!strcmp(op, "creds")){
4115 char *folder;
4116 int colid;
4118 if(objc < 4){
4119 err = "creds: insufficient args";
4121 else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
4122 && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
4123 int i;
4124 CONTEXT_S *cp;
4127 * CMD: creds <collection-index> <folder> [user passwd]
4129 * Test for valid credentials to access given folder
4131 * Returns: 1 if so, 0 otherwise
4134 for(i = 0, cp = wps_global ? wps_global->context_list : NULL;
4135 i < 1 || cp != NULL ;
4136 i++, cp = cp->next)
4137 if(i == colid){
4138 int rv = 0;
4139 char tmp[MAILTMPLEN], *p;
4141 if(cp){
4142 if(folder[0] == '\0'){
4143 if(cp->use & CNTXT_INCMNG)
4144 rv = 1;
4145 else
4146 folder = "fake-fake";
4148 else if((cp->use & CNTXT_INCMNG)
4149 && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4150 folder = p;
4153 if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4154 NETMBX mb;
4156 if(mail_valid_net_parse(tmp, &mb)){
4157 if(objc == 4){ /* check creds */
4158 if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
4159 strcpy(mb.user, p);
4161 if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag)))
4162 rv = 1;
4164 else if(objc == 6){ /* set creds */
4165 char *user, *passwd;
4167 if((user = Tcl_GetStringFromObj(objv[4], NULL))
4168 && (passwd = Tcl_GetStringFromObj(objv[5], NULL))){
4169 if(*mb.user && strcmp(mb.user, user)){
4170 err = "creds: mismatched user names";
4171 break;
4174 alpine_set_passwd(user, passwd, mb.host,
4175 mb.sslflag
4176 || mb.tlsflag
4177 || (wps_global ? F_ON(F_PREFER_ALT_AUTH, wps_global) : 0));
4178 rv = 1;
4180 else {
4181 err = "creds: unable to read credentials";
4182 break;
4185 else{
4186 err = "creds: invalid args";
4187 break;
4192 (void) Tcl_ListObjAppendElement(interp,
4193 Tcl_GetObjResult(interp),
4194 Tcl_NewIntObj(rv));
4195 return(TCL_OK);
4198 err = "creds: Unrecognized collection ID";
4200 else
4201 err = "creds: failure to acquire folder and collection ID";
4203 else if(!strcmp(op, "nocred")){
4204 char *folder;
4205 int colid;
4207 if(!wps_global){
4208 err = "No Session active";
4210 else if(objc != 4){
4211 err = "nocred: wrong number of args";
4213 else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
4214 && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
4215 int i;
4216 CONTEXT_S *cp;
4219 * CMD: nocred <collection-index> <folder>
4221 * Test for valid credentials to access given folder
4223 * Returns: 1 if so, 0 otherwise
4226 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4227 if(i == colid){
4228 int rv = 0;
4229 char tmp[MAILTMPLEN], *p;
4231 if((cp->use & CNTXT_INCMNG)
4232 && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
4233 folder = p;
4235 if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
4236 NETMBX mb;
4238 if(mail_valid_net_parse(tmp, &mb)){
4239 if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
4240 strcpy(mb.user, p);
4242 alpine_clear_passwd(mb.user, mb.host);
4246 (void) Tcl_ListObjAppendElement(interp,
4247 Tcl_GetObjResult(interp),
4248 Tcl_NewIntObj(rv));
4249 return(TCL_OK);
4252 err = "creds: Unrecognized collection ID";
4254 else
4255 err = "creds: failure to acquire folder and collection ID";
4257 else if(!strcmp(op, "acceptcert")){
4258 char *certhost;
4259 STRLIST_S **p;
4261 if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){
4262 for(p = &peCertHosts; *p; p = &(*p)->next)
4265 *p = new_strlist(certhost);
4268 err = "PESession: no server name";
4270 else if(!strcmp(op, "random")){
4271 if(objc != 3){
4272 err = "PESession: random <length>";
4273 } else {
4274 char s[1025];
4275 int l;
4277 if(Tcl_GetIntFromObj(interp,objv[2],&l) != TCL_ERROR){
4278 if(l <= 1024){
4279 Tcl_SetResult(interp, peRandomString(s,l,PRS_MIXED_CASE), TCL_STATIC);
4280 return(TCL_OK);
4282 else
4283 err = "PESession: random length too long";
4285 else
4286 err = "PESession: can't get random length";
4289 else if(!strcmp(op, "authdriver")){
4290 if(objc != 4){
4291 err = "PESession: authdriver {add | remove} drivername";
4292 } else {
4293 char *cmd, *driver;
4295 if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
4296 if((driver = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4297 if(!strcmp(cmd,"enable")){
4298 err = "PESession: authdriver enable disabled for the nonce";
4300 else if(!strcmp(cmd,"disable")){
4301 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){
4302 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver);
4303 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4304 return(TCL_OK);
4306 else{
4307 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver);
4308 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
4309 return(TCL_ERROR);
4312 else
4313 err = "PESession: unknown authdriver operation";
4315 else
4316 err = "PESession: Can't read driver name";
4318 else
4319 err = "PESesions: Can't read authdriver operation";
4322 else if(!strcmp(op, "abandon")){
4324 * CMD: abandon [timeout]
4326 * Returns: nothing
4329 if(objc != 3){
4330 err = "PESession: abandon [timeout]";
4331 } else {
4332 long t;
4334 if(Tcl_GetLongFromObj(interp, objv[2], &t) == TCL_OK){
4335 /* ten second minimum and max of default */
4336 if(t > 0 && t <= PE_INPUT_TIMEOUT){
4337 gPEAbandonTimeout = t;
4338 return(TCL_OK);
4340 else
4341 err = "unrecognized timeout";
4343 else
4344 err = "Can't read timeout";
4347 else if(!strcmp(op, "noexpunge")){
4349 * CMD: noexpunge <state>
4351 * Returns: nothing
4354 if(objc != 3){
4355 err = "PESession: noexpunge <state>";
4356 } else {
4357 int onoff;
4359 if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){
4360 if(onoff == 0 || onoff == 1){
4361 wps_global->noexpunge_on_close = onoff;
4362 return(TCL_OK);
4365 err = "unrecognized on/off state";
4367 else
4368 err = "Can't read on/off state";
4371 else if(!strcmp(op, "setpassphrase")){
4372 #ifdef SMIME
4373 char *passphrase;
4375 if(objc != 3){
4376 err = "PESession: setpassphrase <state>";
4378 else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){
4379 if(wps_global && wps_global->smime){
4380 strncpy((char *) wps_global->smime->passphrase, passphrase,
4381 sizeof(wps_global->smime->passphrase));
4382 wps_global->smime->passphrase[sizeof(wps_global->smime->passphrase)-1] = '\0';
4383 wps_global->smime->entered_passphrase = 1;
4384 wps_global->smime->need_passphrase = 0;
4385 peED.uid = 0;
4386 return(TCL_OK);
4389 #else
4390 err = "S/MIME not configured for this server";
4391 #endif /* SMIME */
4393 else if(!strcmp(op, "expungecheck")) {
4395 * Return open folders and how many deleted messages they have
4397 * return looks something like a list of these:
4398 * {folder-name number-deleted isinbox isincoming}
4400 char *type;
4401 long delete_count;
4402 Tcl_Obj *resObj;
4404 if(objc != 3){
4405 err = "PESession: expungecheck <type>";
4407 else {
4408 type = Tcl_GetStringFromObj(objv[2], NULL);
4409 if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){
4411 if(wps_global->mail_stream != sp_inbox_stream()
4412 || strcmp(type, "current") == 0){
4413 delete_count = count_flagged(wps_global->mail_stream, F_DEL);
4414 resObj = Tcl_NewListObj(0, NULL);
4415 Tcl_ListObjAppendElement(interp, resObj,
4416 Tcl_NewStringObj(pretty_fn(wps_global->cur_folder), -1));
4417 Tcl_ListObjAppendElement(interp, resObj,
4418 Tcl_NewIntObj(delete_count));
4419 Tcl_ListObjAppendElement(interp, resObj,
4420 Tcl_NewIntObj((wps_global->mail_stream
4421 == sp_inbox_stream())
4422 ? 1 : 0));
4423 Tcl_ListObjAppendElement(interp, resObj,
4424 Tcl_NewIntObj((wps_global->context_current->use & CNTXT_INCMNG)
4425 ? 1 : 0));
4426 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4427 resObj);
4429 if(strcmp(type, "quit") == 0){
4430 delete_count = count_flagged(sp_inbox_stream(), F_DEL);
4431 resObj = Tcl_NewListObj(0, NULL);
4432 Tcl_ListObjAppendElement(interp, resObj,
4433 Tcl_NewStringObj("INBOX", -1));
4434 Tcl_ListObjAppendElement(interp, resObj,
4435 Tcl_NewIntObj(delete_count));
4436 Tcl_ListObjAppendElement(interp, resObj,
4437 Tcl_NewIntObj(1));
4438 Tcl_ListObjAppendElement(interp, resObj,
4439 Tcl_NewIntObj(1));
4440 Tcl_ListObjAppendElement(interp,
4441 Tcl_GetObjResult(interp), resObj);
4443 return(TCL_OK);
4445 else
4446 err = "PESession: expungecheck unknown type";
4449 else if(!strcmp(op, "mailcheck")) {
4451 * CMD: mailcheck
4453 * ARGS: reload -- "1" if we're reloading
4454 * (vs. just checking newmail as a side effect
4455 * of building a new page)
4457 * Return list of folders with new or expunged messages
4459 * return looks something like a list of these:
4460 * {new-count newest-uid announcement-msg}
4462 int reload, force = UFU_NONE, rv;
4463 time_t now = time(0);
4465 if(objc <= 3){
4466 if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
4467 reload = 0;
4469 /* minimum 10 second between IMAP pings */
4470 if(!time_of_last_input() || now - time_of_last_input() > 10){
4471 force = UFU_FORCE;
4472 if(!reload)
4473 peMarkInputTime();
4476 peED.interp = interp;
4478 /* check for new mail */
4479 new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG);
4481 if(!reload){ /* announced */
4482 zero_new_mail_count();
4485 return(TCL_OK);
4487 else
4488 err = "PESession: mailcheck <reload>";
4492 Tcl_SetResult(interp, err, TCL_STATIC);
4493 return(TCL_ERROR);
4499 * PEFolderChange - create context's directory chain
4500 * corresponding to list of given obj's
4502 * NOTE: caller should call reset_context_folders(cp) to
4503 * clean up data structures this creates before returning
4506 PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[])
4508 int i;
4509 FDIR_S *fp;
4510 char *folder;
4512 for(i = 0; i < objc; i++) {
4513 folder = Tcl_GetStringFromObj(objv[i], NULL);
4514 if(!folder) {
4515 Tcl_SetResult(interp, "PEFolderChange: Can't read folder", TCL_VOLATILE);
4516 reset_context_folders(cp);
4517 return(TCL_ERROR);
4520 fp = next_folder_dir(cp, folder, 0, NULL); /* BUG: mail_stream? */
4521 fp->desc = folder_lister_desc(cp, fp);
4522 fp->delim = cp->dir->delim;
4523 fp->prev = cp->dir;
4524 fp->status |= CNTXT_SUBDIR;
4525 cp->dir = fp;
4528 return(TCL_OK);
4532 * PEMakeFolderString:
4535 PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath)
4537 int i;
4538 unsigned long size,len;
4539 char *portion,*path;
4541 size = 0;
4542 for(i = 0; i < objc; i++) {
4543 portion = Tcl_GetStringFromObj(objv[i], NULL);
4544 if(!portion) {
4545 Tcl_SetResult(interp, "PEMakeFolderString: Can't read folder",
4546 TCL_VOLATILE);
4547 return(TCL_ERROR);
4549 if(i) size++;
4550 size += strlen(portion);
4553 path = (char*) fs_get(size + 1);
4554 size = 0;
4555 for(i = 0; i < objc; i++) {
4556 portion = Tcl_GetStringFromObj(objv[i], NULL);
4557 len = strlen(portion);
4558 if(i) path[size++] = cp->dir->delim;
4559 memcpy(path + size, portion, len);
4560 size += len;
4562 path[size] = '\0';
4563 if(ppath) *ppath = path; else fs_give((void**) &path);
4564 return(TCL_OK);
4569 * PEFolderCmd - export various bits of folder information
4572 PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
4574 char *op, errbuf[256], *err = "Unknown PEFolder request";
4576 dprint((2, "PEFolderCmd"));
4578 if(objc == 1){
4579 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
4581 else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
4582 if(wps_global){
4583 if(objc == 2){
4584 if(!strcmp(op, "current")){
4585 CONTEXT_S *cp;
4586 int i;
4589 * CMD: current
4591 * Returns: string representing the name of the
4592 * current mailbox
4595 for(i = 0, cp = wps_global->context_list; cp && cp != wps_global->context_current; i++, cp = cp->next)
4598 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK
4599 && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wps_global->cur_folder,-1)) == TCL_OK)
4600 return(TCL_OK);
4602 return(TCL_ERROR);
4604 else if(!strcmp(op, "collections")){
4605 CONTEXT_S *cp;
4606 int i;
4609 * CMD: collections
4611 * Returns: List of currently configured collections
4613 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next){
4614 Tcl_Obj *objv[3];
4616 objv[0] = Tcl_NewIntObj(i);
4617 objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1);
4618 objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1);
4620 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
4621 Tcl_NewListObj(3, objv));
4624 return(TCL_OK);
4626 else if(!strcmp(op, "defaultcollection")){
4627 int i;
4628 CONTEXT_S *cp;
4630 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4631 if(cp->use & CNTXT_SAVEDFLT){
4632 Tcl_SetResult(interp, int2string(i), TCL_STATIC);
4633 return(TCL_OK);
4636 err = "PEFolder: isincoming: Invalid collection ID";
4638 else if(!strcmp(op, "clextended")){
4639 CONTEXT_S *cp;
4640 int i;
4641 char tpath[MAILTMPLEN], *p;
4644 * CMD: clextended
4646 * Returns: Extended list of current collections
4648 * Format:
4649 * 0) Collection Number
4650 * 1) Nickname
4651 * 2) Label
4652 * 3) Basically this is a flag to say if we can edit
4653 * 4) Server
4654 * 5) Path
4655 * 6) View
4658 * had to get rid of this cause the args are changed
4660 * if(strcmp("extended",
4661 * Tcl_GetStringFromObj(objv[2], NULL))){
4662 * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE);
4663 * return(TCL_ERROR);
4666 for(i = 0, cp = wps_global->context_list; cp ;
4667 i++, cp = cp->next){
4668 Tcl_Obj *objv[7];
4670 objv[0] = Tcl_NewIntObj(i);
4671 objv[1] = Tcl_NewStringObj(cp->nickname ?
4672 cp->nickname : "", -1);
4673 objv[2] = Tcl_NewStringObj(cp->label ?
4674 cp->label : "", -1);
4675 objv[3] = Tcl_NewIntObj(cp->var.v ? 1 : 0);
4676 objv[4] = Tcl_NewStringObj(cp->server ?
4677 cp->server : "", -1);
4678 tpath[0] = '\0';
4679 if(cp->context){
4680 strncpy(tpath, (cp->context[0] == '{'
4681 && (p = strchr(cp->context, '}')))
4682 ? ++p
4683 : cp->context, sizeof(tpath));
4684 tpath[sizeof(tpath)-1] = '\0';
4685 if((p = strstr(tpath, "%s")) != NULL)
4686 *p = '\0';
4688 objv[5] = Tcl_NewStringObj(tpath, -1);
4689 objv[6] = Tcl_NewStringObj(cp->dir &&
4690 cp->dir->view.user ?
4691 cp->dir->view.user :
4692 "", -1);
4693 Tcl_ListObjAppendElement(interp,
4694 Tcl_GetObjResult(interp),
4695 Tcl_NewListObj(7, objv));
4698 return(TCL_OK);
4701 else if(objc == 3 && !strcmp(op, "delimiter")){
4702 int colid, i;
4703 char delim[2] = {'\0', '\0'};
4704 CONTEXT_S *cp;
4706 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4707 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4708 if(i == colid){
4709 if(cp->dir && cp->dir->delim)
4710 delim[0] = cp->dir->delim;
4712 break;
4715 Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE);
4716 return(TCL_OK);
4718 else
4719 err = "PEFolder: delimiter: Can't read collection ID";
4721 else if(objc == 3 && !strcmp(op, "isincoming")){
4722 int colid, i;
4723 CONTEXT_S *cp;
4725 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4726 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4727 if(i == colid){
4728 Tcl_SetResult(interp, int2string(((cp->use & CNTXT_INCMNG) != 0)), TCL_STATIC);
4729 return(TCL_OK);
4732 err = "PEFolder: isincoming: Invalid collection ID";
4734 else
4735 err = "PEFolder: isincoming: Can't read collection ID";
4737 else if(objc == 4 && !strcmp(op, "unread")){
4738 char *folder, tmp[MAILTMPLEN];
4739 MAILSTREAM *mstream;
4740 CONTEXT_S *cp;
4741 long colid, i, count = 0, flags = (F_UNSEEN | F_UNDEL);
4742 int our_stream = 0;
4744 * CMD: unread
4746 * Returns: number of unread messages in given
4747 * folder
4749 if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4750 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4751 if(i == colid)
4752 break;
4754 if(cp){
4755 if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4756 /* short circuit INBOX */
4757 if(colid == 0 && !strucmp(folder, "inbox")){
4758 count = count_flagged(sp_inbox_stream(), flags);
4760 else{
4762 * BUG: some sort of caching to prevent open() fore each call?
4763 * does stream cache offset this?
4765 if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4766 && (mstream = same_stream_and_mailbox(tmp, wps_global->mail_stream)))){
4767 long retflags = 0;
4769 wps_global->noshow_error = 1;
4770 our_stream = 1;
4771 mstream = context_open(cp, NULL, folder,
4772 SP_USEPOOL | SP_TEMPUSE| OP_READONLY | OP_SHORTCACHE,
4773 &retflags);
4774 wps_global->noshow_error = 0;
4777 count = count_flagged(mstream, flags);
4779 if(our_stream)
4780 pine_mail_close(mstream);
4783 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4784 return(TCL_OK);
4787 else
4788 err = "PEFolder: unread: Invalid collection ID";
4790 else
4791 err = "PEFolder: unread: Can't read collection ID";
4793 else if(objc == 5 && !strcmp(op, "empty")){
4795 * CMD: empty
4797 * Returns: number of expunge messages
4799 * Arguments: <colnum> <folder> <what>
4800 * where <what> is either <uid>, 'selected', or 'all'
4802 CONTEXT_S *cp;
4803 MAILSTREAM *stream = NULL;
4804 MESSAGECACHE *mc;
4805 MSGNO_S *msgmap;
4806 int colid, i, our_stream = 0;
4807 long uid, raw, count = 0L;
4808 char *errstr = NULL, *what, *folder, *p, tmp[MAILTMPLEN];
4810 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
4811 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4812 if(i == colid) break;
4815 if(cp){
4816 if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4817 if((what = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
4818 /* need to open? */
4819 if(!((context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4820 && (stream = same_stream_and_mailbox(tmp, wps_global->mail_stream)))
4821 || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){
4822 long retflags = 0;
4824 our_stream = 1;
4825 stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags);
4828 if(stream){
4829 msgmap = sp_msgmap(stream);
4831 if(!strucmp(what, "all")){
4832 if(mn_get_total(msgmap)){
4833 agg_select_all(stream, msgmap, NULL, 1);
4834 errstr = peApplyFlag(stream, msgmap, 'd', 0, &count);
4835 if(!errstr)
4836 (void) cmd_expunge_work(stream, msgmap, NULL);
4839 else{
4840 /* little complicated since we don't display deleted state and
4841 * don't want to expunge what's not intended.
4842 * remember what's deleted and restore state on the ones left
4843 * when we're done. shouldn't happen much.
4844 * NOTE: "uid" is NOT a UID in this loop
4846 for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
4847 raw = mn_m2raw(msgmap, uid);
4848 if(!get_lflag(stream, msgmap, uid, MN_EXLD)
4849 && (mc = mail_elt(stream, raw)) != NULL
4850 && mc->deleted){
4851 set_lflag(stream, msgmap, uid, MN_STMP, 1);
4852 mail_flag(stream, long2string(raw), "\\DELETED", 0L);
4854 else
4855 set_lflag(stream, msgmap, uid, MN_STMP, 0);
4858 if(!strucmp(what,"selected")){
4859 if(any_lflagged(msgmap, MN_SLCT)){
4860 if(!(errstr = peApplyFlag(stream, msgmap, 'd', 0, &count)))
4861 (void) cmd_expunge_work(stream, msgmap, NULL);
4863 else
4864 count = 0L;
4866 else{
4867 uid = 0;
4868 for(p = what; *p; p++)
4869 if(isdigit((unsigned char) *p)){
4870 uid = (uid * 10) + (*p - '0');
4872 else{
4873 errstr = "Invalid uid value";
4874 break;
4877 if(!errstr && uid){
4878 /* uid is a UID here */
4879 mail_flag(stream, long2string(uid), "\\DELETED", ST_SET | ST_UID);
4880 (void) cmd_expunge_work(stream, msgmap, NULL);
4881 count = 1L;
4885 /* restore deleted on what didn't get expunged */
4886 for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
4887 raw = mn_m2raw(msgmap, uid);
4888 if(get_lflag(stream, msgmap, uid, MN_STMP)){
4889 set_lflag(stream, msgmap, uid, MN_STMP, 0);
4890 mail_flag(stream, long2string(raw), "\\DELETED", ST_SET);
4895 if(our_stream)
4896 pine_mail_close(stream);
4898 else
4899 errstr = "no stream";
4901 else
4902 errstr = "Cannot get which ";
4904 else
4905 errstr = "Cannot get folder";
4907 else
4908 errstr = "Invalid collection";
4910 if(errstr){
4911 Tcl_SetResult(interp, errstr, TCL_VOLATILE);
4912 return(TCL_ERROR);
4915 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
4916 return(TCL_OK);
4918 else if(!strcmp(op, "export")){
4920 * CMD: export
4922 * Returns: success or failure after writing given
4923 * folder to given local file.
4925 * Format:
4926 * 0) Collection Number
4927 * 1) Folder
4928 * 2) Destination file
4930 if(objc == 5){
4931 CONTEXT_S *cp;
4932 MAILSTREAM *src;
4933 APPEND_PKG pkg;
4934 STRING msg;
4935 long colid, i;
4936 char *folder, *dfile, seq[64], tmp[MAILTMPLEN];
4937 int our_stream = 0;
4939 if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
4940 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
4941 if(i == colid)
4942 break;
4944 if(cp){
4945 if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
4946 if((dfile = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
4947 if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
4949 snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile);
4951 if(pine_mail_create(NULL, tmp)){
4953 err = NULL; /* reset error condition */
4956 * if not current folder, open a stream, setup the
4957 * stuff to write the raw header/text by hand
4958 * with berkeley delimiters since we don't want
4959 * a local mailbox driver lunk in.
4961 * comments:
4962 * - BUG: what about logins?
4965 if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
4966 && (src = same_stream_and_mailbox(tmp, wps_global->mail_stream)))){
4967 long retflags = 0;
4969 our_stream = 1;
4970 src = context_open(cp, NULL, folder,
4971 SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE,
4972 &retflags);
4975 if(src && src->nmsgs){
4976 /* Go to work...*/
4977 pkg.stream = src;
4978 pkg.msgno = 0;
4979 pkg.msgmax = src->nmsgs;
4980 pkg.flags = pkg.date = NIL;
4981 pkg.message = &msg;
4983 snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
4984 mail_fetchfast (src, seq);
4986 wps_global->noshow_error = 1;
4987 if(!mail_append_multiple (NULL, dfile,
4988 peAppendMsg, (void *) &pkg)){
4989 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: export: %.200s",
4990 wps_global->c_client_error);
4993 wps_global->noshow_error = 0;
4995 if(our_stream)
4996 pine_mail_close(src);
4998 else
4999 err = "PEFolder: export: can't open mail folder";
5001 if(!err)
5002 return(TCL_OK);
5004 else
5005 err = "PEFolder: export: can't create destination";
5007 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix"))
5008 err = "PEFolder: export: can't disable driver";
5010 else
5011 err = "PEFolder: export: can't enable driver";
5013 else
5014 err = "PEFolder: export: can't read file name";
5016 else
5017 err = "PEFolder: export: can't read folder name";
5019 else
5020 err = "PEFolder: export: Invalid collection ID";
5022 else
5023 err = "PEFolder:export: Can't read collection ID";
5025 else
5026 err = "PEFolder: export <colid> <folder> <file>";
5028 else if(!strcmp(op, "import")){
5030 * CMD: import
5032 * Returns: success or failure after writing given
5033 * folder to given local file.
5035 * Format:
5036 * 0) source file
5037 * 1) destination collection number
5038 * 2) destination folder
5040 if(objc == 5){
5041 CONTEXT_S *cp;
5042 MAILSTREAM *src, *dst;
5043 APPEND_PKG pkg;
5044 STRING msg;
5045 long colid, i;
5046 char *folder, *sfile, seq[64];
5048 /* get source file with a little sanity check */
5049 if((sfile = Tcl_GetStringFromObj(objv[2], NULL))
5050 && *sfile == '/' && !strstr(sfile, "..")){
5051 if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
5053 wps_global->noshow_error = 1; /* don't queue error msg */
5054 err = NULL; /* reset error condition */
5056 /* make sure sfile contains valid mail */
5057 if((src = mail_open(NULL, sfile, 0L)) != NULL){
5059 if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){
5060 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5061 if(i == colid)
5062 break;
5064 if(cp){
5065 if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
5066 long retflags = 0;
5068 if(context_create(cp, NULL, folder)
5069 && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){
5071 if(src->nmsgs){
5072 /* Go to work...*/
5073 pkg.stream = src;
5074 pkg.msgno = 0;
5075 pkg.msgmax = src->nmsgs;
5076 pkg.flags = pkg.date = NIL;
5077 pkg.message = &msg;
5079 snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
5080 mail_fetchfast (src, seq);
5082 if(!context_append_multiple(cp, dst, folder,
5083 peAppendMsg, (void *) &pkg,
5084 wps_global->mail_stream)){
5085 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5086 wps_global->c_client_error);
5091 pine_mail_close(dst);
5093 else
5094 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5095 wps_global->c_client_error);
5097 else
5098 err = "PEFolder: import: can't read folder name";
5100 else
5101 err = "PEFolder:import: invalid collection id";
5103 else
5104 err = "PEFolder: import: can't read collection id";
5106 mail_close(src);
5109 else
5110 snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
5111 wps_global->c_client_error);
5113 wps_global->noshow_error = 0;
5115 if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err)
5116 err = "PEFolder: import: can't disable driver";
5118 if(!err)
5119 return(TCL_OK);
5121 else
5122 err = "PEFolder: import: can't enable driver";
5124 else
5125 err = "PEFolder: import: can't read file name";
5127 else
5128 err = "PEFolder: import <file> <colid> <folder>";
5130 else {
5131 int i, colid;
5132 char *aes, *colstr;
5133 CONTEXT_S *cp;
5136 * 3 or more arguments, 3rd is the collection ID, rest
5137 * are a folder name
5140 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
5141 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5142 if(i == colid) break;
5144 else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
5145 if(!strcmp("default", colstr))
5146 cp = default_save_context(wps_global->context_list);
5147 else
5148 cp = NULL;
5150 else
5151 cp = NULL;
5153 if(cp){
5154 if(!strcmp(op, "list")){
5155 int i, fcount, bflags = BFL_NONE;
5157 if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR)
5158 return TCL_ERROR;
5160 if(cp->use & CNTXT_NEWS)
5161 bflags |= BFL_LSUB;
5163 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5165 pePrepareForAuthException();
5167 build_folder_list(NULL, cp, "*", NULL, bflags);
5169 if((aes = peAuthException()) != NULL){
5170 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5171 reset_context_folders(cp);
5172 return(TCL_ERROR);
5175 if((fcount = folder_total(FOLDERS(cp))) != 0){
5176 for(i = 0; i < fcount; i++){
5177 char type[3], *p;
5178 FOLDER_S *f = folder_entry(i, FOLDERS(cp));
5180 p = type;
5181 if(f->isdir){
5182 *p++ = 'D';
5184 if(f->hasnochildren && !f->haschildren)
5185 *p++ = 'E';
5188 if(f->isfolder
5189 || f->nickname
5190 || (cp->use & CNTXT_INCMNG))
5191 *p++ = 'F';
5193 *p = '\0';
5195 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type,
5196 f->nickname ? f->nickname : f->name);
5200 reset_context_folders(cp);
5201 return(TCL_OK);
5203 else if(!strucmp(op, "exists")){
5204 char *folder, *errstr = NULL;
5205 int rv;
5207 if(objc < 4) {
5208 Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE);
5209 return(TCL_ERROR);
5211 folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5212 if(!folder) {
5213 Tcl_SetResult(interp, "PEFolder exists: Can't read folder", TCL_VOLATILE);
5214 return(TCL_ERROR);
5217 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5218 return TCL_ERROR;
5220 wps_global->c_client_error[0] = '\0';
5221 pePrepareForAuthException();
5223 rv = folder_name_exists(cp, folder, NULL);
5225 if(rv & FEX_ERROR){
5226 if((errstr = peAuthException()) == NULL){
5227 if(wps_global->c_client_error[0])
5228 errstr = wps_global->c_client_error;
5229 else
5230 errstr = "Indeterminate Error";
5234 Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE);
5235 return(errstr ? TCL_ERROR : TCL_OK);
5237 else if(!strucmp(op, "fullname")){
5238 char *folder, *fullname;
5240 if(objc < 4) {
5241 Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE);
5242 return(TCL_ERROR);
5244 folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5245 if(!folder) {
5246 Tcl_SetResult(interp, "PEFolder fullname: Can't read folder", TCL_VOLATILE);
5247 return(TCL_ERROR);
5250 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5251 return TCL_ERROR;
5253 #if 0
5254 Tcl_Obj *obj = Tcl_NewStringObj((fullname = folder_is_nick(folder, FOLDERS(cp)))
5255 ? fullname : folder, -1);
5256 (void) Tcl_ListObjAppendElement(interp,
5257 Tcl_GetObjResult(interp),
5258 obj);
5259 #else
5260 Tcl_SetResult(interp,
5261 (fullname = folder_is_nick(folder, FOLDERS(cp), FN_NONE)) ? fullname : folder,
5262 TCL_VOLATILE);
5263 #endif
5265 return(TCL_OK);
5267 else if(!strucmp(op, "create")){
5268 char *aes, *folder;
5270 folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5271 if(!folder) {
5272 Tcl_SetResult(interp, "PEFolder create: Can't read folder", TCL_VOLATILE);
5273 return(TCL_ERROR);
5276 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5277 return TCL_ERROR;
5279 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5280 pePrepareForAuthException();
5282 if(!context_create(cp, NULL, folder)){
5283 if((aes = peAuthException()) != NULL){
5284 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5286 else{
5287 Tcl_SetResult(interp,
5288 (wps_global->last_error[0])
5289 ? wps_global->last_error
5290 : (wps_global->c_client_error[0])
5291 ? wps_global->c_client_error
5292 : "Unable to create folder",
5293 TCL_VOLATILE);
5296 reset_context_folders(cp);
5297 return(TCL_ERROR);
5300 Tcl_SetResult(interp, "OK", TCL_STATIC);
5301 reset_context_folders(cp);
5302 return(TCL_OK);
5304 else if(!strucmp(op, "delete")){
5305 int fi, readonly, close_opened = 0;
5306 char *folder, *fnamep, *target = NULL, *aes;
5307 MAILSTREAM *del_stream = NULL, *strm = NULL;
5308 EditWhich ew;
5309 PINERC_S *prc = NULL;
5310 FOLDER_S *fp;
5312 folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5313 if(!folder) {
5314 Tcl_SetResult(interp, "PEFolder delete: Can't read folder", TCL_VOLATILE);
5315 return(TCL_ERROR);
5318 if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
5319 return TCL_ERROR;
5321 /* so we can check for folder's various properties */
5322 build_folder_list(NULL, cp, folder, NULL, BFL_NONE);
5324 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5326 pePrepareForAuthException();
5328 /* close open folder, then delete */
5330 if((fi = folder_index(folder, cp, FI_FOLDER)) < 0
5331 || (fp = folder_entry(fi, FOLDERS(cp))) == NULL){
5332 Tcl_SetResult(interp, "Cannot find folder to delete", TCL_STATIC);
5333 reset_context_folders(cp);
5334 return(TCL_ERROR);
5337 if(!((cp->use & CNTXT_INCMNG) && fp->name
5338 && check_for_move_mbox(fp->name, NULL, 0, &target))){
5339 target = NULL;
5342 dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?"));
5344 ew = config_containing_inc_fldr(fp);
5345 if(wps_global->restricted)
5346 readonly = 1;
5347 else{
5348 switch(ew){
5349 case Main:
5350 prc = wps_global->prc;
5351 break;
5352 case Post:
5353 prc = wps_global->post_prc;
5354 break;
5355 case None:
5356 break;
5359 readonly = prc ? prc->readonly : 1;
5362 if(prc && prc->quit_to_edit && (cp->use & CNTXT_INCMNG)){
5363 Tcl_SetResult(interp, "Must Exit Alpine to Change Configuration", TCL_STATIC);
5364 reset_context_folders(cp);
5365 return(TCL_ERROR);
5368 if(cp == wps_global->context_list
5369 && !(cp->dir && cp->dir->ref)
5370 && strucmp(folder, wps_global->inbox_name) == 0){
5371 Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC);
5372 reset_context_folders(cp);
5373 return(TCL_ERROR);
5375 else if(readonly && (cp->use & CNTXT_INCMNG)){
5376 Tcl_SetResult(interp, "Folder not in editable config file", TCL_STATIC);
5377 reset_context_folders(cp);
5378 return(TCL_ERROR);
5380 else if((fp->name
5381 && (strm=context_already_open_stream(cp,fp->name,AOS_NONE)))
5383 (target
5384 && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
5385 if(strm == wps_global->mail_stream)
5386 close_opened++;
5388 else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
5389 FDIR_S *fdirp = next_folder_dir(cp,folder,TRUE,NULL);
5390 int ret;
5392 if(fp->haschildren)
5393 ret = 1;
5394 else if(fp->hasnochildren)
5395 ret = 0;
5396 else{
5397 ret = folder_total(fdirp->folders) > 0;
5398 free_fdir(&fdirp, 1);
5401 if(ret){
5402 Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC);
5403 reset_context_folders(cp);
5404 return(TCL_ERROR);
5408 * Folder by the same name exist, so delete both...
5409 if(fp->isdual){
5410 Tcl_SetResult(interp, "Cannot delete: folder is also a directory", TCL_STATIC);
5411 reset_context_folders(cp);
5412 return(TCL_ERROR);
5417 if(cp->use & CNTXT_INCMNG){
5418 Tcl_SetResult(interp, "Cannot delete incoming folder", TCL_STATIC);
5419 reset_context_folders(cp);
5420 return(TCL_ERROR);
5423 dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
5424 fp->name ? fp->name : "?",
5425 fp->nickname ? fp->nickname : "",
5426 cp->context ? cp->context : "?"));
5427 if(strm){
5429 * Close it, NULL the pointer, and let do_broach_folder fixup
5430 * the rest...
5432 pine_mail_actually_close(strm);
5433 if(close_opened){
5434 do_broach_folder(wps_global->inbox_name,
5435 wps_global->context_list,
5436 NULL, DB_INBOXWOCNTXT);
5441 * Use fp->name since "folder" may be a nickname...
5443 if(wps_global->mail_stream
5444 && context_same_stream(cp, fp->name, wps_global->mail_stream))
5445 del_stream = wps_global->mail_stream;
5447 fnamep = fp->name;
5449 if(!context_delete(cp, del_stream, fnamep)){
5450 if((aes = peAuthException()) != NULL){
5451 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5453 else{
5454 Tcl_SetResult(interp,
5455 (wps_global->last_error[0])
5456 ? wps_global->last_error
5457 : (wps_global->c_client_error[0])
5458 ? wps_global->c_client_error
5459 : "Unable to delete folder",
5460 TCL_VOLATILE);
5463 reset_context_folders(cp);
5464 return(TCL_ERROR);
5468 Tcl_SetResult(interp, "OK", TCL_STATIC);
5469 reset_context_folders(cp);
5470 return(TCL_OK);
5473 * must be at least 5 arguments for the next set of commands
5475 else if(objc < 5) {
5476 Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE);
5477 return(TCL_ERROR);
5479 else if(!strucmp(op, "rename")){
5480 char *folder,*newfolder, *aes;
5482 folder = Tcl_GetStringFromObj(objv[objc - 2], NULL);
5483 if(!folder) {
5484 Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
5485 return(TCL_ERROR);
5488 newfolder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
5489 if(!newfolder) {
5490 Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
5491 return(TCL_ERROR);
5494 if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR)
5495 return TCL_ERROR;
5497 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
5498 pePrepareForAuthException();
5500 if(!context_rename(cp, NULL, folder, newfolder)){
5501 if((aes = peAuthException()) != NULL){
5502 Tcl_SetResult(interp, aes, TCL_VOLATILE);
5504 else{
5505 Tcl_SetResult(interp,
5506 (wps_global->last_error[0])
5507 ? wps_global->last_error
5508 : (wps_global->c_client_error[0])
5509 ? wps_global->c_client_error
5510 : "Unable to rename folder",
5511 TCL_VOLATILE);
5513 reset_context_folders(cp);
5514 return(TCL_ERROR);
5516 Tcl_SetResult(interp, "OK", TCL_STATIC);
5517 reset_context_folders(cp);
5518 return(TCL_OK);
5521 else
5522 err = "PEFolder: Unrecognized collection ID";
5525 else
5526 err = "No User Context Established";
5529 Tcl_SetResult(interp, err, TCL_VOLATILE);
5530 return(TCL_ERROR);
5535 * PEMailboxCmd - export various bits of mailbox information
5538 PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
5540 char *op, errbuf[256], *err = "Unknown PEMailbox operation";
5542 dprint((5, "PEMailboxCmd"));
5544 if(objc == 1){
5545 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
5547 else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
5548 if(!strucmp(op, "open")){
5549 int i, colid;
5550 char *folder;
5551 CONTEXT_S *cp;
5553 peED.uid = 0; /* forget cached embedded data */
5556 * CMD: open <context-index> <folder>
5560 if(objc == 2){
5561 Tcl_SetResult(interp, (!sp_dead_stream(wps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE);
5562 return(TCL_OK);
5565 if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
5566 if((folder = Tcl_GetStringFromObj(objv[objc - 1], NULL)) != NULL) {
5567 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
5568 if(i == colid) {
5569 if(PEMakeFolderString(interp, cp, objc - 3, objv + 3,
5570 &folder))
5571 return TCL_ERROR;
5573 dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder));
5575 return(peCreateStream(interp, cp, folder, FALSE));
5578 err = "open: Unrecognized collection ID";
5580 else
5581 err = "open: Can't read folder";
5583 else
5584 err = "open: Can't get collection ID";
5586 else if(!strcmp(op, "indexformat")){
5588 * CMD: indexformat
5590 * Returns: list of lists where:
5591 * * the first element is the name of the
5592 * field which may be "From", "Subject"
5593 * "Date" or the empty string.
5594 * * the second element which is either
5595 * the percentage width or empty string
5597 if(objc == 2)
5598 return(peIndexFormat(interp));
5600 else if(wps_global && wps_global->mail_stream){
5601 if(!strcmp(op, "select")){
5602 return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
5604 else if(!strcmp(op, "search")){
5605 return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
5607 else if(!strucmp(op, "apply")){
5608 return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2]));
5610 else if(!strcmp(op, "expunge")){
5612 * CMD: expunge
5614 * Returns: OK after having removed deleted messages
5616 char *streamstr = NULL;
5617 MAILSTREAM *stream;
5618 MSGNO_S *msgmap;
5620 if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5621 if(!streamstr
5622 || (streamstr && (strcmp(streamstr, "current") == 0))){
5623 stream = wps_global->mail_stream;
5624 msgmap = sp_msgmap(stream);
5626 else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5627 stream = sp_inbox_stream();
5628 msgmap = sp_msgmap(stream);
5630 else return(TCL_ERROR);
5631 wps_global->last_error[0] = '\0';
5632 if(IS_NEWS(stream)
5633 && stream->rdonly){
5634 msgno_exclude_deleted(stream, msgmap, NULL);
5635 clear_index_cache(sp_inbox_stream(), 0);
5638 * This is kind of surprising at first. For most sort
5639 * orders, if the whole set is sorted, then any subset
5640 * is also sorted. Not so for OrderedSubject sort.
5641 * If you exclude the first message of a subject group
5642 * then you change the date that group is to be sorted on.
5644 if(mn_get_sort(msgmap) == SortSubject2)
5645 refresh_sort(wps_global->mail_stream, msgmap, FALSE);
5647 else
5648 (void) cmd_expunge_work(stream, msgmap, NULL);
5650 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
5651 return(TCL_OK);
5653 else if(!strcmp(op, "trashdeleted")){
5655 * CMD: trashdeleted
5657 * Returns: OK after moving deleted messages to Trash and expunging
5659 MAILSTREAM *stream;
5660 MESSAGECACHE *mc;
5661 CONTEXT_S *cp;
5662 MSGNO_S *msgmap;
5663 char *streamstr = NULL, tmp[MAILTMPLEN];
5664 long n, tomove = 0L;
5666 if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
5667 if(!streamstr
5668 || (streamstr && (strcmp(streamstr, "current") == 0))){
5669 stream = wps_global->mail_stream;
5670 msgmap = sp_msgmap(stream);
5672 else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
5673 stream = sp_inbox_stream();
5674 msgmap = sp_msgmap(stream);
5676 else return(TCL_ERROR);
5678 wps_global->last_error[0] = '\0';
5679 if(IS_NEWS(stream) && stream->rdonly){
5680 msgno_exclude_deleted(stream, msgmap, NULL);
5681 clear_index_cache(sp_inbox_stream(), 0);
5684 * This is kind of surprising at first. For most sort
5685 * orders, if the whole set is sorted, then any subset
5686 * is also sorted. Not so for OrderedSubject sort.
5687 * If you exclude the first message of a subject group
5688 * then you change the date that group is to be sorted on.
5690 if(mn_get_sort(msgmap) == SortSubject2)
5691 refresh_sort(wps_global->mail_stream, msgmap, FALSE);
5693 else{
5694 if(!(cp = default_save_context(wps_global->context_list)))
5695 cp = wps_global->context_list;
5697 /* copy to trash if we're not in trash */
5698 if(wps_global->VAR_TRASH_FOLDER
5699 && wps_global->VAR_TRASH_FOLDER[0]
5700 && context_allowed(context_apply(tmp, cp, wps_global->VAR_TRASH_FOLDER, sizeof(tmp)))
5701 && !same_stream_and_mailbox(tmp, stream)){
5703 /* save real selected set, and */
5704 for(n = 1L; n <= mn_get_total(msgmap); n++){
5705 set_lflag(stream, msgmap, n, MN_STMP, get_lflag(stream, msgmap, n, MN_SLCT));
5706 /* select deleted */
5707 if(!get_lflag(stream, msgmap, n, MN_EXLD)
5708 && (mc = mail_elt(stream, mn_m2raw(msgmap,n))) != NULL && mc->deleted){
5709 tomove++;
5710 set_lflag(stream, msgmap, n, MN_SLCT, 1);
5712 else
5713 set_lflag(stream, msgmap, n, MN_SLCT, 0);
5716 if(tomove && pseudo_selected(stream, msgmap)){
5718 /* save deleted to Trash */
5719 n = save(wps_global, stream,
5720 cp, wps_global->VAR_TRASH_FOLDER,
5721 msgmap, SV_FOR_FILT | SV_FIX_DELS);
5723 /* then remove them */
5724 if(n == tomove){
5725 (void) cmd_expunge_work(stream, msgmap, NULL);
5728 restore_selected(msgmap);
5731 /* restore selected set */
5732 for(n = 1L; n <= mn_get_total(msgmap); n++)
5733 set_lflag(stream, msgmap, n, MN_SLCT,
5734 get_lflag(stream, msgmap, n, MN_STMP));
5738 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
5739 return(TCL_OK);
5741 else if(!strcmp(op, "nextvector")){
5742 long msgno, count, countdown;
5743 int i, aObjN = 0;
5744 char *errstr = NULL, *s;
5745 Tcl_Obj *rvObj, *vObj, *avObj, **aObj;
5748 * CMD: nextvector
5750 * ARGS: msgno - message number "next" is relative to
5751 * count - how many msgno slots to return
5752 * attrib - (optional) attributes to be returned with each message in vector
5754 * Returns: vector containing next <count> messagenumbers (and optional attributes)
5756 if(objc == 4 || objc == 5){
5757 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) == TCL_OK){
5758 if(Tcl_GetLongFromObj(interp, objv[3], &count) == TCL_OK){
5760 /* set index range for efficiency */
5761 if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
5762 gPeITop = msgno;
5763 gPeICount = count;
5766 if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){
5768 if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0
5769 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))){
5770 mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
5772 for(countdown = count; countdown > 0; countdown--){
5773 imapuid_t uid = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), msgno));
5774 int fetched = 0;
5776 if((vObj = Tcl_NewListObj(0, NULL)) != NULL){
5777 Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno));
5778 peAppListF(interp, vObj, "%lu", uid);
5780 if(aObjN){
5781 if((avObj = Tcl_NewListObj(0, NULL)) != NULL){
5782 for(i = 0; i < aObjN; i++){
5783 if((s = Tcl_GetStringFromObj(aObj[i], NULL)) != NULL){
5784 if(!strcmp(s, "statusbits")){
5785 char *s = peMsgStatBitString(wps_global, wps_global->mail_stream,
5786 sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
5787 gPeITop, gPeICount, &fetched);
5788 Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1));
5790 else if(!strcmp(s, "statuslist")){
5791 Tcl_Obj *nObj = peMsgStatNameList(interp, wps_global, wps_global->mail_stream,
5792 sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
5793 gPeITop, gPeICount, &fetched);
5794 Tcl_ListObjAppendElement(interp, avObj, nObj);
5796 else if(!strcmp(s, "status")){
5797 long raw;
5798 char stat[3];
5799 MESSAGECACHE *mc;
5801 raw = peSequenceNumber(uid);
5803 if(!((mc = mail_elt(wps_global->mail_stream, raw)) && mc->valid)){
5804 mail_fetch_flags(wps_global->mail_stream,
5805 ulong2string(uid), FT_UID);
5806 mc = mail_elt(wps_global->mail_stream, raw);
5809 stat[0] = mc->deleted ? '1' : '0';
5810 stat[1] = mc->recent ? '1' : '0';
5811 stat[2] = mc->seen ? '1' : '0';
5813 Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3));
5815 else if(!strcmp(s, "indexparts")){
5816 Tcl_Obj *iObj;
5818 if((iObj = Tcl_NewListObj(0, NULL)) != NULL
5819 && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK
5820 && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){
5822 else
5823 return(TCL_ERROR);
5825 else if(!strucmp(s, "indexcolor")){
5826 if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK)
5827 return(TCL_ERROR);
5830 else{
5831 errstr = "nextvector: can't read attributes";
5832 break;
5836 Tcl_ListObjAppendElement(interp, vObj, avObj);
5838 else{
5839 errstr = "nextvector: can't allocate attribute return vector";
5840 break;
5844 else{
5845 errstr = "nextvector: can't allocate new vector";
5846 break;
5849 Tcl_ListObjAppendElement(interp, rvObj, vObj);
5851 for(++msgno; msgno <= mn_get_total(sp_msgmap(wps_global->mail_stream)) && msgline_hidden(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_NONE); msgno++)
5854 if(msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))
5855 break;
5859 if(!errstr){
5860 /* append result vector */
5861 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), rvObj);
5862 /* Everything is coerced to UTF-8 */
5863 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
5864 Tcl_NewStringObj("UTF-8", -1));
5865 return(TCL_OK);
5868 else
5869 errstr = "nextvector: can't read attribute list";
5871 else
5872 errstr = "nextvector: can't read count";
5874 else
5875 errstr = "nextvector: can't read message number";
5877 else
5878 errstr = "nextvector: Incorrect number of arguments";
5880 if(errstr)
5881 Tcl_SetResult(interp, errstr, TCL_STATIC);
5883 return(TCL_ERROR);
5885 else if(objc == 2){
5886 if(!strcmp(op, "messagecount")){
5888 * CMD: messagecount
5890 * Returns: count of messages in open mailbox
5892 Tcl_SetResult(interp,
5893 long2string(mn_get_total(sp_msgmap(wps_global->mail_stream))),
5894 TCL_VOLATILE);
5895 return(TCL_OK);
5897 else if(!strcmp(op, "firstinteresting")){
5899 * CMD: firstinteresting
5901 * Returns: message number associated with
5902 * "incoming-startup-rule" which had better
5903 * be the "current" message since it was set
5904 * in do_broach_folder and shouldn't have been
5905 * changed otherwise (expunged screw us?)
5907 Tcl_SetResult(interp,
5908 long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
5909 TCL_VOLATILE);
5910 return(TCL_OK);
5912 else if(!strcmp(op, "selected")){
5914 * CMD: selected
5916 * Returns: count of selected messages in open mailbox
5919 Tcl_SetResult(interp,
5920 long2string(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT)),
5921 TCL_VOLATILE);
5922 return(TCL_OK);
5924 else if(!strcmp(op, "searched")){
5926 * CMD: searched
5928 * Returns: count of searched messages in open mailbox
5931 Tcl_SetResult(interp,
5932 long2string(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH)),
5933 TCL_VOLATILE);
5934 return(TCL_OK);
5936 else if(!strcmp(op, "mailboxname")){
5938 * CMD: name
5940 * Returns: string representing the name of the
5941 * current mailbox
5943 Tcl_SetResult(interp, wps_global->cur_folder, TCL_VOLATILE);
5944 return(TCL_OK);
5946 else if(!strcmp(op, "close")){
5948 * CMD: close
5950 * Returns: with global mail_stream closed
5952 peDestroyStream(wps_global);
5953 return(TCL_OK);
5955 else if(!strcmp(op, "newmailreset")){
5956 sml_seen();
5957 zero_new_mail_count();
5958 sp_set_mail_box_changed(wps_global->mail_stream, 0);
5959 sp_set_expunge_count(wps_global->mail_stream, 0L);
5960 peMarkInputTime();
5961 return(TCL_OK);
5963 else if(!strcmp(op, "newmailstatmsg")){
5964 long newest, count;
5965 char subject[500], subjtxt[500], from[500], intro[500], *s = "";
5968 * CMD: newmailstatmsg
5970 * ARGS: none
5972 * Returns: text for new mail message
5976 if(sp_mail_box_changed(wps_global->mail_stream)
5977 && (count = sp_mail_since_cmd(wps_global->mail_stream))){
5979 for(newest = wps_global->mail_stream->nmsgs; newest > 1L; newest--)
5980 if(!get_lflag(wps_global->mail_stream, NULL, newest, MN_EXLD))
5981 break;
5983 if(newest){
5984 format_new_mail_msg(NULL, count,
5985 pine_mail_fetchstructure(wps_global->mail_stream,
5986 newest, NULL),
5987 intro, from, subject, subjtxt, sizeof(subject));
5989 snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt);
5993 Tcl_SetResult(interp, s, TCL_VOLATILE);
5994 return(TCL_OK);
5996 else if(!strcmp(op, "savedefault")){
5997 return(peSaveDefault(interp, 0L, 0, NULL));
5999 else if(!strcmp(op, "gotodefault")){
6000 return(peGotoDefault(interp, 0L, NULL));
6002 else if(!strcmp(op, "zoom")){
6003 Tcl_SetResult(interp,
6004 long2string((any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L)
6005 ? any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT) : 0L),
6006 TCL_VOLATILE);
6007 return(TCL_OK);
6009 else if(!strcmp(op, "focus")){
6010 Tcl_SetResult(interp,
6011 long2string((any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L)
6012 ? any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH) : 0L),
6013 TCL_VOLATILE);
6014 return(TCL_OK);
6016 else if(!strcmp(op, "first")){
6017 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE)){
6018 long n;
6020 for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++)
6021 if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE)){
6022 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6023 return(TCL_OK);
6026 unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6030 Tcl_SetResult(interp, int2string(1), TCL_VOLATILE);
6031 return(TCL_OK);
6033 else if(!strucmp(op, "current")){
6034 long n = 0;
6035 unsigned long u = 0;
6038 * CMD: current
6040 * ARGS:
6042 * Returns: list of current msg {<sequence> <uid>}
6045 if(mn_total_cur(sp_msgmap(wps_global->mail_stream)) <= 0
6046 || ((n = mn_get_cur(sp_msgmap(wps_global->mail_stream))) > 0
6047 && (u = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), n))) > 0)){
6048 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
6049 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(u), -1));
6050 return(TCL_OK);
6052 else
6053 err = "Cannot get current";
6055 else if(!strcmp(op, "last")){
6056 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE)){
6057 long n;
6059 for(n = mn_get_total(sp_msgmap(wps_global->mail_stream)); n > 0L; n--)
6060 if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE)){
6061 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6062 return(TCL_OK);
6065 else{
6066 Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6067 return(TCL_OK);
6070 Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC);
6071 return(TCL_ERROR);
6073 else if(!strucmp(op, "sortstyles")){
6074 int i;
6076 * CMD: sortstyles
6078 * Returns: list of supported sort styles
6081 for(i = 0; wps_global->sort_types[i] != EndofList; i++)
6082 if(Tcl_ListObjAppendElement(interp,
6083 Tcl_GetObjResult(interp),
6084 Tcl_NewStringObj(sort_name(wps_global->sort_types[i]), -1)) != TCL_OK)
6085 return(TCL_ERROR);
6087 return(TCL_OK);
6089 else if(!strucmp(op, "sort")){
6090 return(peAppendCurrentSort(interp));
6092 else if(!strucmp(op, "state")){
6093 if(!wps_global->mail_stream || sp_dead_stream(wps_global->mail_stream))
6094 Tcl_SetResult(interp, "closed", TCL_STATIC);
6095 else if(wps_global->mail_stream->rdonly && !IS_NEWS(wps_global->mail_stream))
6096 Tcl_SetResult(interp, "readonly", TCL_STATIC);
6097 else
6098 Tcl_SetResult(interp, "ok", TCL_STATIC);
6100 return(TCL_OK);
6102 else if(!strucmp(op, "excludedeleted")){
6103 msgno_exclude_deleted(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), NULL);
6104 return(TCL_OK);
6107 else if(objc == 3){
6108 if(!strcmp(op, "uid")){
6109 long msgno, raw;
6112 * Return uid of given message number
6114 * CMD: uid <msgnumber>
6117 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK)
6118 return(TCL_ERROR); /* conversion problem? */
6120 if((raw = mn_m2raw(sp_msgmap(wps_global->mail_stream), msgno)) > 0L){
6121 raw = mail_uid(wps_global->mail_stream, raw);
6122 Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE);
6123 return(TCL_OK);
6126 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno);
6127 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
6128 return(TCL_ERROR);
6130 else if(!strcmp(op, "newmail")){
6131 int reload, force = UFU_NONE, rv;
6132 time_t now = time(0);
6135 * CMD: newmail
6137 * ARGS: reload -- "1" if we're reloading
6138 * (vs. just checking newmail as a side effect
6139 * of building a new page)
6141 * Returns: count -
6142 * mostrecent -
6144 if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
6145 reload = 0;
6147 /* minimum 10 second between IMAP pings */
6148 if(!time_of_last_input() || now - time_of_last_input() > 10){
6149 force = UFU_FORCE;
6150 peMarkInputTime();
6153 /* check for new mail */
6154 new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE);
6156 rv = peNewMailResult(interp);
6158 if(!reload) /* announced */
6159 zero_new_mail_count();
6161 return(rv);
6163 else if(!strcmp(op, "flagcount")){
6164 char *flag;
6165 long count = 0L;
6166 long flags = 0L;
6167 int objlc;
6168 Tcl_Obj **objlv;
6172 * CMD: flagcount
6174 * ARGS: flags -
6176 * Returns: count - number of message thusly flagged
6177 * mostrecent -
6179 if(Tcl_ListObjGetElements(interp, objv[2], &objlc, &objlv) == TCL_OK){
6180 while(objlc--)
6181 if((flag = Tcl_GetStringFromObj(*objlv++, NULL)) != NULL){
6182 if(!strucmp(flag, "deleted")){
6183 flags |= F_DEL;
6185 if(!strucmp(flag, "undeleted")){
6186 flags |= F_UNDEL;
6188 else if(!strucmp(flag, "seen")){
6189 flags |= F_SEEN;
6191 else if(!strucmp(flag, "unseen")){
6192 flags |= F_UNSEEN;
6194 else if(!strucmp(flag, "flagged")){
6195 flags |= F_FLAG;
6197 else if(!strucmp(flag, "unflagged")){
6198 flags |= F_UNFLAG;
6200 else if(!strucmp(flag, "answered")){
6201 flags |= F_ANS;
6203 else if(!strucmp(flag, "unanswered")){
6204 flags |= F_UNANS;
6206 else if(!strucmp(flag, "recent")){
6207 flags |= F_RECENT;
6209 else if(!strucmp(flag, "unrecent")){
6210 flags |= F_UNRECENT;
6214 if(flags)
6215 count = count_flagged(wps_global->mail_stream, flags);
6218 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6219 return(TCL_OK);
6221 else if(!strcmp(op, "zoom")){
6222 int newstate;
6223 long n, zoomed = 0L;
6226 * CMD: zoom
6228 * Set/clear HID bits of non SLCT messages as requested.
6229 * PEMailbox [first | last | next] are sensitive to these flags.
6231 * ARGS: newstate - 1 or 0
6233 * Returns: count of zoomed messages
6236 if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6237 if(newstate > 0){
6238 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(wps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT)))){
6239 zoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MN_SLCT);
6240 zoomed = n;
6243 else{
6244 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
6245 unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6249 Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6250 return(TCL_OK);
6252 else if(!strcmp(op, "focus")){
6253 int newstate;
6254 long n, zoomed = 0L;
6257 * CMD: focus
6259 * Set/clear HID bits of non MN_SRCH messages as requested.
6260 * PEMailbox [first | last | next] are sensitive to MN_HIDE flag
6262 * ARGS: newstate - 1 or 0
6264 * Returns: count of zoomed messages
6267 if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
6268 if(newstate > 0){
6269 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(wps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SRCH))))
6270 zoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MN_SRCH);
6272 zoomed = n;
6274 else{
6275 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
6276 unzoom_index(wps_global, wps_global->mail_stream, sp_msgmap(wps_global->mail_stream));
6280 Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
6281 return(TCL_OK);
6283 else if(!strcmp(op, "next")){
6284 long msgno;
6287 * CMD: next <msgno>
6289 * ARGS: msgno - message number "next" is relative to
6291 * Returns: previous state
6294 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){
6295 mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6296 mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6297 Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6298 return(TCL_OK);
6301 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6302 return(TCL_ERROR);
6305 else if(objc == 4){
6306 if(!strucmp(op, "sort")){
6307 int i, reversed = 0;
6308 char *sort;
6311 * CMD: sort sortstyle reversed
6313 * Returns: OK with the side-effect of message
6314 * numbers now reflecting the requested
6315 * sort order.
6318 if((sort = Tcl_GetStringFromObj(objv[2], NULL))
6319 && Tcl_GetIntFromObj(interp, objv[3], &reversed) != TCL_ERROR){
6320 /* convert sort string into */
6321 for(i = 0; wps_global->sort_types[i] != EndofList; i++)
6322 if(strucmp(sort_name(wps_global->sort_types[i]), sort) == 0){
6323 if(sp_unsorted_newmail(wps_global->mail_stream)
6324 || !(wps_global->sort_types[i] == mn_get_sort(sp_msgmap(wps_global->mail_stream))
6325 && mn_get_revsort(sp_msgmap(wps_global->mail_stream)) == reversed))
6326 sort_folder(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
6327 wps_global->sort_types[i],
6328 reversed, 0);
6330 break;
6334 return(peAppendCurrentSort(interp));
6336 else if(!strucmp(op, "selected")){
6337 return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
6339 else if(!strucmp(op, "searched")){
6340 return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
6342 else if(!strcmp(op, "next")){
6343 long msgno, count;
6346 * CMD: next
6348 * ARGS: msgno - message number "next" is relative to
6349 * count - how many to increment it
6351 * Returns: previous state
6354 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6355 && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6356 mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6357 while(count)
6358 if(count > 0){
6359 mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6360 count--;
6362 else{
6363 mn_dec_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6364 count++;
6367 Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))), TCL_VOLATILE);
6368 return(TCL_OK);
6371 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6372 return(TCL_ERROR);
6374 else if(!strcmp(op, "x-nextvector")){
6375 long msgno, count;
6378 * CMD: nextvector
6380 * ARGS: msgno - message number "next" is relative to
6381 * count - how many msgno slots to return
6383 * Returns: vector containing next <count> messagenumbers
6386 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6387 && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6388 if(count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream)))){
6389 mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6391 while(count--){
6392 long n = mn_get_cur(sp_msgmap(wps_global->mail_stream));
6394 if(peAppListF(interp, Tcl_GetObjResult(interp),
6395 "%l%l", n, mail_uid(wps_global->mail_stream,
6396 mn_m2raw(sp_msgmap(wps_global->mail_stream), n))) != TCL_OK)
6397 return(TCL_ERROR);
6399 mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6401 if(n == mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6402 break;
6406 return(TCL_OK);
6409 Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
6410 return(TCL_ERROR);
6412 else if(!strcmp(op, "messagecount")){
6413 char *relative;
6414 long msgno, n, count = 0L;
6417 * CMD: messagecount
6419 * ARGS: [before | after] relative to
6420 * msgno
6422 * Returns: count of messages before or after given message number
6425 if((relative = Tcl_GetStringFromObj(objv[2], NULL))
6426 && Tcl_GetLongFromObj(interp, objv[3], &msgno) != TCL_ERROR){
6427 if(msgno < 1L || msgno > mn_get_total(sp_msgmap(wps_global->mail_stream))){
6428 Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC);
6429 return(TCL_ERROR);
6432 if(!strucmp(relative, "before")){
6433 for(n = msgno - 1; n > 0L; n--)
6434 if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE))
6435 count++;
6437 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6438 return(TCL_OK);
6440 else if(!strucmp(relative, "after")){
6441 for(n = msgno + 1; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++)
6442 if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_HIDE))
6443 count++;
6445 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
6446 return(TCL_OK);
6450 Tcl_SetResult(interp, "can't read range for count", TCL_STATIC);
6451 return(TCL_ERROR);
6453 else if(!strcmp(op, "selectvector")){
6454 long msgno, count;
6457 * CMD: selectvector
6459 * ARGS: msgno - message number "next" is relative to
6460 * count - how many msgno slots to return
6462 * Returns: vector containing next <count> messagenumbers
6465 if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
6466 && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
6467 if(msgno > 0L){
6468 mn_set_cur(sp_msgmap(wps_global->mail_stream), msgno);
6469 while(count--){
6470 msgno = mn_get_cur(sp_msgmap(wps_global->mail_stream));
6472 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_SLCT))
6473 if(Tcl_ListObjAppendElement(interp,
6474 Tcl_GetObjResult(interp),
6475 Tcl_NewLongObj((long) mail_uid(wps_global->mail_stream, msgno))) != TCL_OK)
6476 return(TCL_ERROR);
6478 mn_inc_cur(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), MH_NONE);
6480 if(msgno == mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6481 break;
6485 return(TCL_OK);
6488 Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC);
6489 return(TCL_ERROR);
6491 else if(!strucmp(op, "current")){
6492 char *which;
6493 long x, n = 0, u = 0;
6496 * CMD: current
6498 * ARGS: (number|uid) <msgno>
6500 * Returns: list of current msg {<sequence> <uid>}
6502 if((which = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
6503 if(Tcl_GetLongFromObj(interp, objv[3], &x) == TCL_OK){
6504 if(!strucmp(which,"uid")){
6505 u = x;
6506 n = peMessageNumber(u);
6508 else if(!strucmp(which,"number")){
6509 n = x;
6510 u = mail_uid(wps_global->mail_stream, mn_m2raw(sp_msgmap(wps_global->mail_stream), n));
6513 if(n && u){
6514 mn_set_cur(sp_msgmap(wps_global->mail_stream), n);
6515 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
6516 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(u), -1));
6517 return(TCL_OK);
6519 else
6520 err = "PEMailbox current: invalid number/uid";
6522 else
6523 err = "PEMailbox current: cannot get number";
6525 else
6526 err = "PEMailbox current: cannot get which";
6529 else
6530 err = "PEMailbox: Too many arguments";
6532 else if(!strucmp(op, "name") || !strcmp(op, "close")){
6533 Tcl_SetResult(interp, "", TCL_STATIC);
6534 return(TCL_OK);
6536 else
6537 snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox",
6538 Tcl_GetStringFromObj(objv[0], NULL), op);
6541 Tcl_SetResult(interp, err, TCL_VOLATILE);
6542 return(TCL_ERROR);
6547 peAppendCurrentSort(Tcl_Interp *interp)
6549 return((Tcl_ListObjAppendElement(interp,
6550 Tcl_GetObjResult(interp),
6551 Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(wps_global->mail_stream))), -1)) == TCL_OK
6552 && Tcl_ListObjAppendElement(interp,
6553 Tcl_GetObjResult(interp),
6554 Tcl_NewStringObj(mn_get_revsort(sp_msgmap(wps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK)
6555 ? TCL_OK : TCL_ERROR);
6560 peAppendDefaultSort(Tcl_Interp *interp)
6562 return((Tcl_ListObjAppendElement(interp,
6563 Tcl_GetObjResult(interp),
6564 Tcl_NewStringObj(sort_name(wps_global->def_sort), -1)) == TCL_OK
6565 && Tcl_ListObjAppendElement(interp,
6566 Tcl_GetObjResult(interp),
6567 Tcl_NewStringObj(wps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK)
6568 ? TCL_OK : TCL_ERROR);
6573 peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6575 char *subcmd;
6576 long n, i, diff, msgno;
6577 int narrow, hidden;
6578 MESSAGECACHE *mc;
6579 extern MAILSTREAM *mm_search_stream;
6580 extern long mm_search_count;
6582 hidden = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE) > 0L;
6583 mm_search_stream = wps_global->mail_stream;
6584 mm_search_count = 0L;
6586 for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6587 if((mc = mail_elt(wps_global->mail_stream, n)) != NULL){
6588 mc->searched = 0;
6589 mc->spare7 = 1;
6593 * CMD: select
6595 * ARGS: subcmd subcmdargs
6597 * Returns: flip "matchflag" private bit on all or none
6598 * of the messages in the mailbox
6600 if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
6601 if(!strucmp(subcmd, "all")){
6603 * Args: <none>
6605 if(matchflag & MN_SLCT){
6606 if(objc != 1)
6607 return(peSelectError(interp, subcmd));
6609 agg_select_all(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), NULL, 1);
6611 else if(matchflag & MN_SRCH){
6612 for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6613 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, matchflag, 1);
6616 Tcl_SetResult(interp, "All", TCL_VOLATILE);
6618 else if(!strucmp(subcmd, "none")){
6620 * Args: <none>
6622 n = 0L;
6624 if(matchflag & MN_SLCT){
6625 if(objc != 1)
6626 return(peSelectError(interp, subcmd));
6628 agg_select_all(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), &n, 0);
6630 else if(matchflag & MN_SRCH){
6631 for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6632 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, matchflag, 0);
6635 Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
6637 else if(!strucmp(subcmd, "searched")){
6639 * Args: <none>
6641 for(n = 1L, i = 0; n <= wps_global->mail_stream->nmsgs; n++)
6642 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SRCH)){
6643 i++;
6644 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SLCT, 1);
6647 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
6649 else if(!strucmp(subcmd, "unsearched")){
6651 * Args: <none>
6653 for(n = 1L, i = 0; n <= wps_global->mail_stream->nmsgs; n++)
6654 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SRCH)){
6655 i++;
6656 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), n, MN_SLCT, 0);
6659 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
6661 else{
6662 if(!strucmp(subcmd, "narrow"))
6663 narrow = 1;
6664 else if(!strucmp(subcmd, "broad"))
6665 narrow = 0;
6666 else
6667 return(peSelectError(interp, "invalid scope request"));
6669 if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL)))
6670 return(peSelectError(interp, "missing subcommand"));
6672 if(!strucmp(subcmd, "num")){
6673 if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6674 return(i);
6676 else if(!strucmp(subcmd, "date")){
6677 if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6678 return(i);
6680 else if(!strucmp(subcmd, "text")){
6681 if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6682 return(i);
6684 else if(!strucmp(subcmd, "status")){
6685 if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
6686 return(i);
6688 else if(!strucmp(subcmd, "compound")){
6689 char *s;
6690 int nSearchList, nSearch;
6691 Tcl_Obj **oSearchList, **oSearch;
6693 /* BUG: should set up one SEARCHPGM to fit criteria and issue single search */
6695 if(Tcl_ListObjGetElements(interp, objv[2], &nSearchList, &oSearchList) == TCL_OK){
6696 for(i = 0; i < nSearchList; i++){
6697 if(Tcl_ListObjGetElements(interp, oSearchList[i], &nSearch, &oSearch) == TCL_OK){
6698 if((s = Tcl_GetStringFromObj(oSearch[0], NULL)) != NULL){
6699 if(!strucmp(s,"date")){
6700 if((n = peSelectDate(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6701 return(n);
6703 else if(!strucmp(s,"text")){
6704 if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6705 return(n);
6707 else if(!strucmp(s,"status")){
6708 if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
6709 return(n);
6711 else
6712 return(peSelectError(interp, "unknown compound search"));
6714 /* logical AND the results */
6715 mm_search_count = 0L;
6716 for(n = 1L; n <= wps_global->mail_stream->nmsgs; n++)
6717 if((mc = mail_elt(wps_global->mail_stream, n)) != NULL){
6718 if(mc->searched && mc->spare7)
6719 mm_search_count++;
6720 else
6721 mc->searched = mc->spare7 = 0;
6724 else
6725 return(peSelectError(interp, "malformed compound search"));
6727 else
6728 return(peSelectError(interp, "malformed compound search"));
6731 else
6732 return(peSelectError(interp, "malformed compound search"));
6734 else
6735 return(peSelectError(interp, "cmd cmdargs"));
6738 * at this point all interesting messages should
6739 * have searched bit lit
6742 if(narrow) /* make sure something was selected */
6743 for(i = 1L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
6744 if(mail_elt(wps_global->mail_stream,
6745 mn_m2raw(sp_msgmap(wps_global->mail_stream), i))->searched){
6746 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag))
6747 break;
6748 else
6749 mm_search_count--;
6752 diff = 0L;
6753 if(mm_search_count){
6755 * loop thru all the messages, adjusting local flag bits
6756 * based on their "searched" bit...
6758 for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
6759 if(narrow){
6760 /* turning OFF selectedness if the "searched" bit isn't lit. */
6761 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
6762 if(!mail_elt(wps_global->mail_stream,
6763 mn_m2raw(sp_msgmap(wps_global->mail_stream), i))->searched){
6764 diff--;
6765 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag, 0);
6766 if(hidden)
6767 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, MN_HIDE, 1);
6769 else if(msgno < mn_get_cur(sp_msgmap(wps_global->mail_stream)))
6770 msgno = i;
6773 else if(mail_elt(wps_global->mail_stream,mn_m2raw(sp_msgmap(wps_global->mail_stream),i))->searched){
6774 /* turn ON selectedness if "searched" bit is lit. */
6775 if(!get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
6776 diff++;
6777 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag, 1);
6778 if(hidden)
6779 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, MN_HIDE, 0);
6783 /* if we're zoomed and the current message was unselected */
6784 if(narrow && msgno
6785 && get_lflag(wps_global->mail_stream,sp_msgmap(wps_global->mail_stream),mn_get_cur(sp_msgmap(wps_global->mail_stream)),MN_HIDE))
6786 mn_reset_cur(sp_msgmap(wps_global->mail_stream), msgno);
6789 Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE);
6792 return(TCL_OK);
6795 Tcl_SetResult(interp, "Can't read select option", TCL_STATIC);
6796 return(TCL_ERROR);
6800 peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6803 * Args: [broad | narrow] firstnumber lastnumber
6806 long first = 0L, last = 0L, n;
6808 if(objc == 2){
6809 if(Tcl_GetLongFromObj(interp, objv[0], &first) == TCL_OK
6810 && Tcl_GetLongFromObj(interp, objv[1], &last) == TCL_OK){
6811 if(last && last < first){
6812 n = last;
6813 last = first;
6814 first = n;
6817 if(first >= 1L && first <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
6818 if(last){
6819 if(last >= 1L && last <= mn_get_total(sp_msgmap(wps_global->mail_stream))){
6820 for(n = first; n <= last; n++)
6821 mm_searched(wps_global->mail_stream,
6822 mn_m2raw(sp_msgmap(wps_global->mail_stream), n));
6824 else
6825 return(peSelectError(interp, "last out of range"));
6827 else{
6828 mm_searched(wps_global->mail_stream,
6829 mn_m2raw(sp_msgmap(wps_global->mail_stream), first));
6832 else
6833 return(peSelectError(interp, "first out of range"));
6835 else
6836 return(peSelectError(interp, "can't read first/last"));
6838 else
6839 return(peSelectError(interp, "num first last"));
6841 return(TCL_OK);
6845 peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6848 * Args: [broad | narrow]
6849 * tense - "on", "since", "before"
6850 * year - 4 digit year
6851 * month - abbreviated month "jan", "feb"...
6852 * day - day number
6855 char *tense, *year, *month, *day, buf[256];
6857 if(objc == 4){
6858 if((tense = peSelValTense(objv[0])) != NULL){
6859 if((year = peSelValYear(objv[1])) != NULL){
6860 if((month = peSelValMonth(objv[2])) != NULL){
6861 if((day = peSelValDay(objv[3])) != NULL){
6862 snprintf(buf, sizeof(buf), "%s %s-%s-%s", tense, day, month, year);
6863 pine_mail_search_full(wps_global->mail_stream, NULL,
6864 mail_criteria(buf),
6865 SE_NOPREFETCH | SE_FREE);
6867 else
6868 return(peSelectError(interp, "<with valid day>"));
6870 else
6871 return(peSelectError(interp, "<with valid month>"));
6873 else
6874 return(peSelectError(interp, "<with valid year>"));
6876 else
6877 return(peSelectError(interp, "<with valid tense>"));
6879 else
6880 return(peSelectError(interp, "date tense year monthabbrev daynum"));
6882 return(TCL_OK);
6886 peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6889 * Args: [broad | narrow]
6890 * case - in not
6891 * field - to from cc recip partic subj any
6892 * text - free text search string
6894 int not;
6895 char field, *text;
6897 if(objc == 3){
6898 if((not = peSelValCase(objv[0])) >= 0){
6899 if((field = peSelValField(objv[1])) != '\0'){
6900 if((text = Tcl_GetStringFromObj(objv[2], NULL))
6901 && strlen(text) < 1024){
6902 /* BUG: fix charset not to be NULL below */
6903 if(agg_text_select(wps_global->mail_stream,
6904 sp_msgmap(wps_global->mail_stream),
6905 field, NULL, not, 0, text, NULL, NULL))
6906 /* BUG: plug in "charset" above? */
6907 return(peSelectError(interp, "programmer botch"));
6909 else
6910 return(peSelectError(interp, "<with search string < 1024>"));
6912 else
6913 return(peSelectError(interp, "<with valid field>"));
6915 else
6916 return(peSelectError(interp, "<with valid case>"));
6918 else
6919 return(peSelectError(interp, "text case field text"));
6921 return(TCL_OK);
6925 peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
6928 * Args: [broad | narrow]
6929 * case - on not
6930 * status - imp new ans del
6932 int not;
6933 char flag;
6935 if(objc == 2){
6936 if((not = peSelValCase(objv[0])) >= 0){
6937 if((flag = peSelValFlag(objv[1])) != '\0'){
6938 if(agg_flag_select(wps_global->mail_stream, not, flag, NULL))
6939 return(peSelectError(interp, "programmer botch"));
6941 else
6942 return(peSelectError(interp, "<with valid flag>"));
6944 else
6945 return(peSelectError(interp, "<with valid case>"));
6947 else
6948 return(peSelectError(interp, "status focus case flag"));
6950 return(TCL_OK);
6953 char *
6954 peSelValTense(Tcl_Obj *objp)
6956 char *tense, **pp;
6958 if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){
6959 static char *tenses[] = {"on", "since", "before", NULL};
6961 for(pp = tenses; *pp; pp++)
6962 if(!strucmp(*pp, tense))
6963 return(tense);
6966 return(NULL);
6970 char *
6971 peSelValYear(Tcl_Obj *objp)
6973 char *year;
6975 return((year = Tcl_GetStringFromObj(objp, NULL))
6976 && strlen(year) == 4
6977 && isdigit((unsigned char) year[0])
6978 && isdigit((unsigned char) year[0])
6979 && isdigit((unsigned char) year[0])
6980 ? year
6981 : NULL);
6985 char *
6986 peSelValMonth(Tcl_Obj *objp)
6988 char *month, **pp;
6989 static char *mons[] = {"jan","feb","mar","apr",
6990 "may","jun","jul","aug",
6991 "sep","oct","nov","dec", NULL};
6993 if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3)
6994 for(pp = mons; *pp; pp++)
6995 if(!strucmp(month, *pp))
6996 return(*pp);
6998 return(NULL);
7002 char *
7003 peSelValDay(Tcl_Obj *objp)
7005 char *day;
7007 return(((day = Tcl_GetStringFromObj(objp, NULL))
7008 && (day[0] == '0' || day[0] == '1'
7009 || day[0] == '2' || day[0] == '3')
7010 && isdigit((unsigned char) day[1])
7011 && day[2] == '\0')
7012 ? day
7013 : NULL);
7018 peSelValCase(Tcl_Obj *objp)
7020 char *not;
7022 if((not = Tcl_GetStringFromObj(objp, NULL)) != NULL){
7023 if(!strucmp(not, "ton"))
7024 return(0);
7025 else if(!strucmp(not, "not"))
7026 return(1);
7029 return(-1);
7034 peSelValField(Tcl_Obj *objp)
7036 char *field;
7037 int i;
7038 static struct {
7039 char *field;
7040 int type;
7041 } fields[] = {{"from", 'f'},
7042 {"to", 't'},
7043 {"cc", 'c'},
7044 {"subj", 's'},
7045 {"any", 'a'},
7046 {"recip", 'r'},
7047 {"partic", 'p'},
7048 {"body", 'b'},
7049 {NULL,0}};
7051 if((field = Tcl_GetStringFromObj(objp, NULL)) != NULL)
7052 for(i = 0; fields[i].field ; i++)
7053 if(!strucmp(fields[i].field, field))
7054 return(fields[i].type);
7056 return(0);
7061 peSelValFlag(Tcl_Obj *objp)
7063 char *flag;
7064 int i;
7065 static struct {
7066 char *flag;
7067 int type;
7068 } flags[] = {{"imp", '*'},
7069 {"new", 'n'},
7070 {"ans", 'a'},
7071 {"del", 'd'},
7072 {NULL,0}};
7074 if((flag = Tcl_GetStringFromObj(objp, NULL)) != NULL)
7075 for(i = 0; flags[i].flag ; i++)
7076 if(!strucmp(flags[i].flag, flag))
7077 return(flags[i].type);
7079 return(0);
7083 peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
7085 int rv = 0;
7086 long i, n;
7087 char *range;
7090 * CMD: searched [before | after] #
7092 * Returns: 1 if criteria is true, 0 otherwise
7095 if((range = Tcl_GetStringFromObj(objv[0], NULL))
7096 && Tcl_GetLongFromObj(interp, objv[1], &n) != TCL_ERROR){
7097 if(!strucmp(range, "before")){
7098 for(i = 1L; i < n && i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
7099 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
7100 rv = 1;
7101 break;
7104 Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7105 return(TCL_OK);
7107 else if(!strucmp(range, "after")){
7108 for(i = n + 1L; i <= mn_get_total(sp_msgmap(wps_global->mail_stream)); i++)
7109 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), i, matchflag)){
7110 rv = 1;
7111 break;
7114 Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
7115 return(TCL_OK);
7119 Tcl_SetResult(interp, "searched test failed", TCL_STATIC);
7120 return(TCL_ERROR);
7125 peSelectError(Tcl_Interp *interp, char *usage)
7127 char buf[256];
7129 snprintf(buf, sizeof(buf), "should be select %.128s", usage);
7130 Tcl_SetResult(interp, buf, TCL_VOLATILE);
7131 return(TCL_ERROR);
7136 peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv)
7138 char *subcmd;
7139 long n;
7141 if(!(n = any_lflagged(sp_msgmap(wps_global->mail_stream), MN_SLCT))){
7142 Tcl_SetResult(interp, "No messages selected", TCL_STATIC);
7143 return(TCL_ERROR);
7145 else if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
7146 if(objc == 1){
7147 if(!strucmp(subcmd, "delete")){
7148 /* BUG: is CmdWhere arg always right? */
7149 (void) cmd_delete(wps_global, sp_msgmap(wps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL);
7150 Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7151 return(TCL_OK);
7153 else if(!strucmp(subcmd, "undelete")){
7154 (void) cmd_undelete(wps_global, sp_msgmap(wps_global->mail_stream), MCMD_AGG | MCMD_SILENT);
7155 Tcl_SetResult(interp, long2string(n), TCL_STATIC);
7156 return(TCL_OK);
7159 else if(objc == 2){
7160 if(!strucmp(subcmd, "count")){
7162 * Args: flag
7164 char *flagname;
7165 long n, rawno, count = 0;
7167 if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
7168 for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++){
7169 rawno = mn_m2raw(sp_msgmap(wps_global->mail_stream), n);
7170 if(get_lflag(wps_global->mail_stream, NULL, rawno, MN_SLCT)
7171 && peIsFlagged(wps_global->mail_stream,
7172 mail_uid(wps_global->mail_stream, rawno),
7173 flagname)){
7174 count++;
7179 Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
7180 return(TCL_OK);
7183 else if(objc == 3){
7184 if(!strucmp(subcmd, "flag")){
7186 * Args: case - on not
7187 * flag - imp new ans del
7189 char flag, *result;
7190 int not;
7191 long flagged;
7193 if((not = peSelValCase(objv[1])) >= 0){
7194 if((flag = peSelValFlag(objv[2])) != '\0'){
7195 result = peApplyFlag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), flag, not, &flagged);
7196 if(!result){
7197 Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE);
7198 return(TCL_OK);
7200 else
7201 return(peApplyError(interp, result));
7203 else
7204 return(peApplyError(interp, "invalid flag"));
7206 else
7207 return(peApplyError(interp, "invalid case"));
7209 else if(!strucmp(subcmd, "save")){
7211 * Args: colid -
7212 * folder - imp new ans del
7215 int colid, flgs = 0, i;
7216 char *folder, *err;
7217 CONTEXT_S *cp;
7219 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7221 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7222 if(i == colid){
7223 if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7224 if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7226 if(!READONLY_FOLDER(wps_global->mail_stream)
7227 && F_OFF(F_SAVE_WONT_DELETE, wps_global))
7228 flgs |= SV_DELETE;
7230 if(colid == 0 && !strucmp(folder, "inbox"))
7231 flgs |= SV_INBOXWOCNTXT;
7233 i = save(wps_global, wps_global->mail_stream,
7234 cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7236 err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem saving";
7238 restore_selected(sp_msgmap(wps_global->mail_stream));
7239 if(err)
7240 return(peApplyError(interp, err));
7242 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7243 return(TCL_OK);
7245 else
7246 return(peApplyError(interp, "can't select"));
7248 else
7249 return(peApplyError(interp, "no folder name"));
7252 return(peApplyError(interp, "bad colid"));
7254 else
7255 return(peApplyError(interp, "invalid case"));
7257 else if(!strucmp(subcmd, "copy")){
7259 * Args: colid -
7260 * folder - imp new ans del
7263 int colid, flgs = 0, i;
7264 char *folder, *err;
7265 CONTEXT_S *cp;
7267 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7269 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7270 if(i == colid){
7271 if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7272 if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7274 if(colid == 0 && !strucmp(folder, "inbox"))
7275 flgs |= SV_INBOXWOCNTXT;
7277 i = save(wps_global, wps_global->mail_stream,
7278 cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7280 err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem copying";
7282 restore_selected(sp_msgmap(wps_global->mail_stream));
7283 if(err)
7284 return(peApplyError(interp, err));
7286 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7287 return(TCL_OK);
7289 else
7290 return(peApplyError(interp, "can't select"));
7292 else
7293 return(peApplyError(interp, "no folder name"));
7296 return(peApplyError(interp, "bad colid"));
7298 else
7299 return(peApplyError(interp, "invalid case"));
7301 else if(!strucmp(subcmd, "move")){
7303 * Args: colid -
7304 * folder - imp new ans del
7307 int colid, flgs = 0, i;
7308 char *folder, *err;
7309 CONTEXT_S *cp;
7311 if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
7313 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
7314 if(i == colid){
7315 if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7316 if(pseudo_selected(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream))){
7318 flgs = SV_DELETE;
7320 if(colid == 0 && !strucmp(folder, "inbox"))
7321 flgs |= SV_INBOXWOCNTXT;
7323 i = save(wps_global, wps_global->mail_stream,
7324 cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
7326 err = (i == mn_total_cur(sp_msgmap(wps_global->mail_stream))) ? NULL : "problem moving";
7328 restore_selected(sp_msgmap(wps_global->mail_stream));
7329 if(err)
7330 return(peApplyError(interp, err));
7332 Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
7333 return(TCL_OK);
7335 else
7336 return(peApplyError(interp, "can't select"));
7338 else
7339 return(peApplyError(interp, "no folder name"));
7342 return(peApplyError(interp, "bad colid"));
7344 else
7345 return(peApplyError(interp, "invalid case"));
7347 else if(!strucmp(subcmd, "spam")){
7349 * Args: spamaddr -
7350 * spamsubj -
7352 char *spamaddr, *spamsubj = NULL;
7353 long n, rawno;
7355 if((spamaddr = Tcl_GetStringFromObj(objv[1], NULL))
7356 && (spamsubj = Tcl_GetStringFromObj(objv[2], NULL))){
7357 for(n = 1L; n <= mn_get_total(sp_msgmap(wps_global->mail_stream)); n++){
7358 rawno = mn_m2raw(sp_msgmap(wps_global->mail_stream), n);
7359 if(get_lflag(wps_global->mail_stream, NULL, rawno, MN_SLCT)){
7360 char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
7362 if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){
7363 Tcl_SetResult(interp, rs, TCL_VOLATILE);
7364 return(TCL_ERROR);
7370 Tcl_SetResult(interp, "OK", TCL_VOLATILE);
7371 return(TCL_OK);
7376 return(peApplyError(interp, "unknown option"));
7380 char *
7381 peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged)
7383 char *seq, *flagstr;
7384 long flags, flagid;
7386 switch (flag) {
7387 case '*' :
7388 flagstr = "\\FLAGGED";
7389 flags = not ? 0L : ST_SET;
7390 flagid = not ? F_FLAG : F_UNFLAG;
7391 break;
7392 case 'n' :
7393 flagstr = "\\SEEN";
7394 flags = not ? ST_SET : 0L;
7395 flagid = not ? F_UNSEEN : F_SEEN;
7396 break;
7397 case 'a' :
7398 flagstr = "\\ANSWERED";
7399 flags = not ? 0L : ST_SET;
7400 flagid = not ? F_ANS : F_UNANS;
7401 break;
7402 case 'd':
7403 flagstr = "\\DELETED";
7404 flags = not ? 0L : ST_SET;
7405 flagid = not ? F_DEL : F_UNDEL;
7406 break;
7407 default :
7408 return("unknown flag");
7409 break;
7412 if(pseudo_selected(stream, msgmap)){
7413 if((seq = currentf_sequence(stream, msgmap, flagid, flagged, 1, NULL, NULL)) != NULL){
7414 mail_flag(stream, seq, flagstr, flags);
7415 fs_give((void **) &seq);
7418 restore_selected(msgmap);
7419 return(NULL);
7421 else
7422 return("can't select");
7427 peApplyError(Tcl_Interp *interp, char *usage)
7429 char buf[256];
7431 snprintf(buf, sizeof(buf), "apply error: %.128s", usage);
7432 Tcl_SetResult(interp, buf, TCL_VOLATILE);
7433 return(TCL_ERROR);
7438 * peIndexFormat - Return with interp's result object set to
7439 * represent the index line's format as a list of
7440 * index-field-name, percentage-width pairs
7443 peIndexFormat(Tcl_Interp *interp)
7445 INDEX_COL_S *cdesc = NULL;
7446 char *name, wbuf[4], *dname;
7448 for(cdesc = wps_global->index_disp_format;
7449 cdesc->ctype != iNothing;
7450 cdesc++) {
7451 dname = NULL;
7452 switch(cdesc->ctype){
7453 case iFStatus:
7454 case iIStatus:
7455 case iSIStatus:
7456 dname = "iStatus";
7457 case iStatus:
7458 name = "Status";
7459 break;
7461 case iMessNo:
7462 name = "Number";
7463 break;
7465 case iPrio:
7466 case iPrioAlpha:
7467 case iPrioBang:
7468 name = "Priority";
7469 break;
7471 case iDate: case iSDate: case iSTime: case iLDate:
7472 case iS1Date: case iS2Date: case iS3Date: case iS4Date: case iDateIso:
7473 case iDateIsoS:
7474 case iSDateIso: case iSDateIsoS:
7475 case iSDateS1: case iSDateS2:
7476 case iSDateS3: case iSDateS4:
7477 case iSDateTime:
7478 case iSDateTimeIso: case iSDateTimeIsoS:
7479 case iSDateTimeS1: case iSDateTimeS2:
7480 case iSDateTimeS3: case iSDateTimeS4:
7481 case iSDateTime24:
7482 case iSDateTimeIso24: case iSDateTimeIsoS24:
7483 case iSDateTimeS124: case iSDateTimeS224:
7484 case iSDateTimeS324: case iSDateTimeS424:
7485 case iCurDate: case iCurDateIso: case iCurDateIsoS:
7486 case iCurTime24: case iCurTime12:
7487 case iCurPrefDate:
7488 name = "Date";
7489 break;
7491 case iCurDay: case iCurDay2Digit:
7492 case iCurDayOfWeek: case iCurDayOfWeekAbb:
7493 name = "Day";
7494 break;
7496 case iCurMon: case iCurMon2Digit:
7497 case iCurMonLong: case iCurMonAbb:
7498 name= "Month";
7499 break;
7501 case iTime24: case iTime12: case iTimezone:
7502 case iCurPrefTime:
7503 name = "Time";
7504 break;
7506 case iDay2Digit: case iDayOfWeek: case iDayOfWeekAbb:
7507 name = "Day";
7508 break;
7510 case iMonAbb: case iMon2Digit:
7511 name = "Month";
7512 break;
7514 case iYear: case iYear2Digit:
7515 case iCurYear: case iCurYear2Digit:
7516 name = "Year";
7517 break;
7519 case iScore :
7520 name = "Score";
7521 break;
7523 case iFromTo:
7524 case iFromToNotNews:
7525 case iFrom:
7526 name = "From";
7527 break;
7529 case iTo:
7530 case iToAndNews :
7531 name = "To";
7532 break;
7534 case iCc:
7535 name = "Cc";
7536 break;
7538 case iRecips:
7539 name = "Recipients";
7540 break;
7542 case iSender:
7543 name = "Sender";
7544 break;
7546 case iSize :
7547 case iSizeComma :
7548 case iSizeNarrow :
7549 case iDescripSize:
7550 case iKSize :
7551 name = "Size";
7552 break;
7554 case iAtt:
7555 name = "Attachments";
7556 break;
7558 case iAddress :
7559 name = "Address";
7560 break;
7562 case iMailbox :
7563 name = "Mailbox";
7564 break;
7566 case iOpeningText:
7567 case iOpeningTextNQ:
7568 name = "Excerpt";
7569 break;
7571 case iSubject :
7572 case iSubjKey :
7573 case iSubjKeyInit :
7574 case iSubjectText :
7575 case iSubjKeyText :
7576 case iShortSubject :
7577 case iShortSubjKey :
7578 case iSubjKeyInitText :
7579 case iShortSubjKeyInit :
7580 name = "Subject";
7581 break;
7583 case iNews:
7584 case iNewsAndTo :
7585 name = "News";
7586 break;
7588 case iNewsAndRecips:
7589 name = "News/Recip";
7590 break;
7592 case iRecipsAndNews:
7593 name = "Recip/News";
7594 break;
7596 default :
7597 name = "";
7598 break;
7601 if(cdesc->width > 0){
7602 int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH);
7604 snprintf(wbuf, sizeof(wbuf), "%d%%", p);
7606 else
7607 wbuf[0] = '\0';
7609 if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK)
7610 return(TCL_ERROR);
7613 return(TCL_OK);
7618 peNewMailResult(Tcl_Interp *interp)
7620 unsigned long n, uid;
7622 if(sp_mail_box_changed(wps_global->mail_stream)){
7623 if((n = sp_mail_since_cmd(wps_global->mail_stream)) != 0L){
7624 /* first element is count of new messages */
7625 if(Tcl_ListObjAppendElement(interp,
7626 Tcl_GetObjResult(interp),
7627 Tcl_NewLongObj(n)) != TCL_OK)
7628 return(TCL_ERROR);
7630 /* second element is UID of most recent message */
7631 for(uid = wps_global->mail_stream->nmsgs; uid > 1L; uid--)
7632 if(!get_lflag(wps_global->mail_stream, NULL, uid, MN_EXLD))
7633 break;
7635 if(!uid){
7636 Tcl_ResetResult(interp);
7637 Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7638 return(TCL_ERROR);
7641 uid = mail_uid(wps_global->mail_stream, uid);
7643 if(Tcl_ListObjAppendElement(interp,
7644 Tcl_GetObjResult(interp),
7645 Tcl_NewLongObj(uid)) != TCL_OK)
7646 return(TCL_ERROR);
7648 else {
7649 if(Tcl_ListObjAppendElement(interp,
7650 Tcl_GetObjResult(interp),
7651 Tcl_NewIntObj(0)) != TCL_OK)
7652 return(TCL_ERROR);
7654 /* zero is UID of new message */
7655 if(Tcl_ListObjAppendElement(interp,
7656 Tcl_GetObjResult(interp),
7657 Tcl_NewIntObj(0)) != TCL_OK)
7658 return(TCL_ERROR);
7661 /* third element is expunge count */
7662 if(Tcl_ListObjAppendElement(interp,
7663 Tcl_GetObjResult(interp),
7664 Tcl_NewLongObj(sp_expunge_count(wps_global->mail_stream)
7665 ? sp_expunge_count(wps_global->mail_stream)
7666 : 0L)) != TCL_OK)
7667 return(TCL_ERROR);
7670 else
7671 Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
7673 return(TCL_OK);
7677 /* * * * * * * * Start of Per-Thread/SubThread access functions * * * * * * * */
7681 * PEThreadCmd - access/manipulate various pieces of thread state
7684 PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7686 char *err, errbuf[256], *cmd, *op;
7687 long uidl;
7688 imapuid_t uid;
7690 dprint((2, "PEThreadCmd"));
7692 snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7693 Tcl_GetStringFromObj(objv[0], NULL));
7695 if(!(wps_global && wps_global->mail_stream)){
7696 snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7697 Tcl_GetStringFromObj(objv[0], NULL));
7699 else if(objc < 2){
7700 Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?");
7702 else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7703 return(TCL_ERROR); /* conversion problem? */
7705 else if(!peSequenceNumber(uidl)){
7706 snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7707 Tcl_GetStringFromObj(objv[0], NULL), uidl);
7709 else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7710 uid = uidl;
7711 if(objc == 3){
7712 if(!strucmp(cmd,"info")){
7713 #define WP_MAX_THRD_PREFIX 256
7714 long raw;
7715 PINETHRD_S *pthrd;
7716 char tstr[WP_MAX_THRD_PREFIX];
7718 if((raw = peSequenceNumber(uid)) != 0L){
7720 * translate PINETHRD_S data into
7722 if((pthrd = msgno_thread_info(wps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){
7724 tstr[0] = '\0';
7725 /* BUG: build tstr form pthrd */
7728 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7729 Tcl_NewStringObj(tstr, -1));
7732 else
7733 Tcl_SetResult(interp, "0", TCL_STATIC);
7735 return(TCL_OK);
7739 else if(objc == 5){
7740 if(!strucmp(cmd,"flag")){
7741 if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
7742 if(!strucmp(op,"deleted")){
7743 int value;
7745 if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){
7746 long n;
7747 PINETHRD_S *pthrd;
7748 char *flag;
7750 while(1){
7751 if(!(n = peSequenceNumber(uid))){
7752 Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC);
7753 return(TCL_ERROR);
7756 flag = cpystr("\\DELETED");
7757 mail_flag(wps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L));
7758 fs_give((void **) &flag);
7760 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
7761 Tcl_NewStringObj(ulong2string(uid), -1));
7763 if(++n <= wps_global->mail_stream->nmsgs){
7764 uid = mail_uid(wps_global->mail_stream, n);
7766 else
7767 break;
7769 if((pthrd = msgno_thread_info(wps_global->mail_stream, n, NULL,THD_TOP)) != NULL){
7771 else
7772 break;
7781 Tcl_SetResult(interp, err, TCL_STATIC);
7782 return(TCL_ERROR);
7787 /* * * * * * * * Start of Per-Message access functions * * * * * * * */
7791 static struct _message_cmds {
7792 char *cmd;
7793 int hcount;
7794 struct {
7795 int argcount;
7796 int (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
7797 } h[3];
7798 } message_cmds[] = {
7799 {"size", 1, {{3, peMessageSize}}},
7800 {"date", 2, {{3, peMessageDate}, {4, peMessageDate}}},
7801 {"subject", 1, {{3, peMessageSubject}}},
7802 {"fromaddr", 1, {{3, peMessageFromAddr}}},
7803 {"toaddr", 1, {{3, peMessageToAddr}}},
7804 {"ccaddr", 1, {{3, peMessageCcAddr}}},
7805 {"status", 1, {{3, peMessageStatus}}},
7806 {"statusbits", 1, {{3, peMessageStatusBits}}},
7807 {"charset", 1, {{3, peMessageCharset}}},
7808 {"number", 1, {{3, peMsgnoFromUID}}},
7809 {"envelope", 0},
7810 {"rawenvelope", 0},
7811 {"text", 1, {{3, peMessageText}}},
7812 {"header", 1, {{3, peMessageHeader}}},
7813 {"attachments", 1, {{3, peMessageAttachments}}},
7814 {"body", 3, {{3, peMessageBody}, {4, peMessageBody}}},
7815 {"cid", 1, {{4, peMessagePartFromCID}}},
7816 {"flag", 2, {{4, peGetFlag}, {5, peSetFlag}}},
7817 {"replyheaders", 2, {{3, peReplyHeaders},{4, peReplyHeaders}}},
7818 {"replytext", 2, {{4, peReplyText}, {5, peReplyText}}},
7819 {"forwardheaders", 2, {{3, peForwardHeaders}, {4, peForwardHeaders}}},
7820 {"forwardtext", 2, {{3, peForwardText}, {4, peForwardText}}},
7821 {"rawbody", 0},
7822 {"select", 2, {{3, peMsgSelect}, {4, peMsgSelect}}},
7823 {"detach", 1, {{5, peDetach}}},
7824 {"attachinfo", 1, {{4, peAttachInfo}}},
7825 {"savedefault", 1, {{3, peSaveDefault}}},
7826 {"save", 1, {{5, peSave}}},
7827 {"copy", 1, {{5, peCopy}}},
7828 {"move", 1, {{5, peMove}}},
7829 {"takeaddr", 1, {{3, peTakeaddr}}},
7830 {"takefrom", 1, {{3, peTakeFrom}}},
7831 {"replyquote", 1, {{3, peReplyQuote}}},
7832 {"bounce", 2, {{4, peMessageBounce},{5, peMessageBounce}}},
7833 {"spam", 1, {{5, peMessageSpamNotice}}},
7834 {"needpasswd", 1, {{3, peMessageNeedPassphrase}}},
7835 {NULL, 0}
7842 * PEMessageCmd - export various bits of message information
7844 * NOTE: all exported commands are of the form:
7846 * PEMessage <uid> <cmd> <args>
7849 PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
7851 char *err, errbuf[256], *cmd;
7852 int i, j;
7853 long uidl;
7854 imapuid_t uid;
7856 dprint((5, "PEMessageCmd"));
7858 snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
7859 Tcl_GetStringFromObj(objv[0], NULL));
7861 if(!(wps_global && wps_global->mail_stream)){
7862 snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
7863 Tcl_GetStringFromObj(objv[0], NULL));
7865 else if(objc < 3){
7866 Tcl_WrongNumArgs(interp, 0, objv, "PEMessage <uid> cmd ?args?");
7868 else if(Tcl_GetLongFromObj(interp, objv[1], &uidl) != TCL_OK){
7869 return(TCL_ERROR); /* conversion problem? */
7871 else if(!peMessageNumber(uidl)){
7872 snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
7873 Tcl_GetStringFromObj(objv[0], NULL), uidl);
7875 else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
7876 uid = uidl;
7877 for(i = 0; message_cmds[i].cmd; i++)
7878 if(!strcmp(cmd, message_cmds[i].cmd)){
7879 for(j = 0; j < message_cmds[i].hcount; j++)
7880 if(message_cmds[i].h[j].argcount == objc)
7881 return((*message_cmds[i].h[j].f)(interp, uid, objc - 3,
7882 &((Tcl_Obj **)objv)[3]));
7884 snprintf(err = errbuf, sizeof(errbuf),
7885 "PEMessage: %s: mismatched argument count", cmd);
7886 break;
7890 Tcl_SetResult(interp, err, TCL_STATIC);
7891 return(TCL_ERROR);
7896 * return the uid's ordinal number within the CURRENT SORT
7898 long
7899 peMessageNumber(imapuid_t uid)
7901 return(mn_raw2m(sp_msgmap(wps_global->mail_stream), peSequenceNumber(uid)));
7905 * return the uid's RAW message number (for c-client reference, primarily)
7907 long
7908 peSequenceNumber(imapuid_t uid)
7910 return(mail_msgno(wps_global->mail_stream, uid));
7915 peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7917 long raw;
7919 if((raw = peSequenceNumber(uid))
7920 && pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL)){
7921 Tcl_SetResult(interp,
7922 long2string(mail_elt(wps_global->mail_stream,
7923 raw)->rfc822_size),
7924 TCL_VOLATILE);
7926 else
7927 Tcl_SetResult(interp, "0", TCL_STATIC);
7929 return(TCL_OK);
7934 peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7936 char *cmd;
7937 long raw;
7938 ENVELOPE *env;
7939 MESSAGECACHE mc;
7941 if((raw = peSequenceNumber(uid))
7942 && (env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL))){
7943 if(objc == 1 && objv[0]){
7944 if(mail_parse_date(&mc, env->date)){
7945 if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
7946 if(!strucmp(cmd,"day")){
7947 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day);
7948 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
7949 return(TCL_OK);
7951 else if(!strucmp(cmd,"month")){
7952 Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE);
7953 return(TCL_OK);
7955 else if(!strucmp(cmd,"year")){
7956 Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE);
7957 return(TCL_OK);
7959 else{
7960 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd);
7961 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
7964 else
7965 Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC);
7967 else
7968 Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC);
7970 else{
7971 Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE);
7972 return(TCL_OK);
7975 else
7976 Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC);
7978 return(TCL_ERROR);
7983 peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7985 return(peMessageField(interp, uid, "from"));
7990 peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7992 return(peMessageField(interp, uid, "to"));
7997 peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
7999 return(peMessageField(interp, uid, "cc"));
8004 peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8006 return(peMessageField(interp, uid, "subject"));
8011 peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field)
8013 long raw;
8014 char *s = "";
8015 ENVELOPE *env;
8017 if((raw = peSequenceNumber(uid))
8018 && (env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL))){
8019 if(!strucmp(field, "from")){
8020 if(env->from && env->from->mailbox)
8021 snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox,
8022 (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : "");
8024 else if(!strucmp(field, "to")){
8025 if(env->to && env->to->mailbox)
8026 snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox,
8027 (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : "");
8029 else if(!strucmp(field, "cc")){
8030 if(env->cc && env->cc->mailbox)
8031 snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox,
8032 (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : "");
8034 else if(!strucmp(field, "subject")){
8035 if(env->subject)
8036 snprintf(s = wtmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject);
8038 else{
8039 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field);
8040 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
8041 return(TCL_ERROR);
8044 Tcl_SetResult(interp, s, TCL_VOLATILE);
8045 return(TCL_OK);
8048 Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC);
8049 return(TCL_ERROR);
8054 peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8056 long raw;
8057 MESSAGECACHE *mc;
8059 if((raw = peSequenceNumber(uid)) != 0L){
8060 if(!((mc = mail_elt(wps_global->mail_stream, raw))
8061 && mc->valid)){
8062 mail_fetch_flags(wps_global->mail_stream,
8063 ulong2string(uid), FT_UID);
8064 mc = mail_elt(wps_global->mail_stream, raw);
8067 if (mc->deleted)
8068 Tcl_ListObjAppendElement(interp,
8069 Tcl_GetObjResult(interp),
8070 Tcl_NewStringObj("Deleted", -1));
8072 if (mc->answered)
8073 Tcl_ListObjAppendElement(interp,
8074 Tcl_GetObjResult(interp),
8075 Tcl_NewStringObj("Answered", -1));
8077 if (!mc->seen)
8078 Tcl_ListObjAppendElement(interp,
8079 Tcl_GetObjResult(interp),
8080 Tcl_NewStringObj("New", -1));
8082 if (mc->flagged)
8083 Tcl_ListObjAppendElement(interp,
8084 Tcl_GetObjResult(interp),
8085 Tcl_NewStringObj("Important", -1));
8088 return(TCL_OK);
8093 peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8095 /* everything coming out of pith better be utf-8 */
8096 Tcl_SetResult(interp, "UTF-8", TCL_STATIC);
8097 return(TCL_OK);
8102 peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8104 #ifdef SMIME
8105 return((wps_global && wps_global->smime && wps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR);
8106 #else
8107 return(TCL_ERROR);
8108 #endif /* SMIME */
8113 peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8115 Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE);
8116 return(TCL_OK);
8121 * peInterpWritec - collect filtered output, appending to the
8122 * command's result list on each EOL
8125 peInterpWritec(int c)
8127 unsigned char ch = (unsigned char) (0xff & c);
8129 if(ch == '\n')
8130 return(peInterpFlush() == TCL_OK);
8131 else
8132 so_writec(ch, peED.store);
8134 return(1);
8139 * peInterpFlush - write accumulated line to result object mapping
8140 * embedded data into exportable tcl list members
8144 peInterpFlush(void)
8146 char *line, *p, *tp, *tp2, col1[32], col2[32];
8147 Tcl_Obj *lobjp, *objColor, *objPair;
8149 line = (char *) so_text(peED.store);
8151 if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){
8152 if((p = strindex(line, TAG_EMBED)) != NULL){
8154 *p = '\0';
8156 if(p - line)
8157 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8159 switch(*++p){
8160 case TAG_HANDLE :
8162 int i, n;
8163 HANDLE_S *h;
8166 for(n = 0, i = *++p; i > 0; i--)
8167 n = (n * 10) + (*++p - '0');
8169 line = ++p; /* prepare for next section of line */
8171 if(!peED.inhandle){
8172 peED.inhandle = 1;
8174 if((h = get_handle(peED.handles, n)) != NULL)
8175 switch(h->type){
8176 case IMG :
8178 Tcl_Obj *llObj, *rObj;
8180 llObj = Tcl_NewListObj(0, NULL);
8181 Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1));
8183 rObj = Tcl_NewListObj(0, NULL);
8184 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1));
8185 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1));
8187 Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8189 Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8190 peED.inhandle = 0;
8193 break;
8195 case URL :
8197 Tcl_Obj *llObj, *rObj;
8199 llObj = Tcl_NewListObj(0, NULL);
8200 Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1));
8202 rObj = Tcl_NewListObj(0, NULL);
8203 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1));
8204 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1));
8206 Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
8208 Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
8211 break;
8213 case Attach :
8215 Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj;
8217 alObj = Tcl_NewListObj(0, NULL);
8218 Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1));
8220 peGetMimeTyping(mail_body(wps_global->mail_stream,
8221 peSequenceNumber(peED.uid),
8222 (unsigned char *) h->h.attach->number),
8223 &tObj, &stObj, &fnObj, &eObj);
8226 rObj = Tcl_NewListObj(0, NULL);
8227 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid));
8228 Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1));
8229 Tcl_ListObjAppendElement(peED.interp, rObj, tObj);
8230 Tcl_ListObjAppendElement(peED.interp, rObj, stObj);
8231 Tcl_ListObjAppendElement(peED.interp, rObj, fnObj);
8232 Tcl_ListObjAppendElement(peED.interp, rObj, eObj);
8234 Tcl_ListObjAppendElement(peED.interp, alObj, rObj);
8236 Tcl_ListObjAppendElement(peED.interp, lobjp, alObj);
8239 break;
8241 default :
8242 break;
8247 break;
8249 case TAG_FGCOLOR :
8250 if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){
8251 /* look ahead */
8252 if(p[11] == TAG_EMBED
8253 && p[12] == TAG_BGCOLOR
8254 && (tp2 = peColorStr(p + 13, col2))){
8255 objColor = Tcl_NewListObj(0, NULL);
8256 objPair = Tcl_NewListObj(0, NULL);
8257 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8258 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8259 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8260 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8261 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8262 strcpy(peED.color.bg, tp2);
8263 p += 13;
8265 else if(strcmp(peED.color.bg, peED.color.bgdef)){
8266 objColor = Tcl_NewListObj(0, NULL);
8267 objPair = Tcl_NewListObj(0, NULL);
8268 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8269 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8270 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1));
8271 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8272 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8273 strcpy(peED.color.bg, peED.color.bgdef);
8275 else
8276 peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp);
8278 strcpy(peED.color.fg, tp);
8281 line = p + 11;
8282 break;
8284 case TAG_BGCOLOR :
8285 if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){
8286 /* look ahead */
8287 if(p[11] == TAG_EMBED
8288 && p[12] == TAG_FGCOLOR
8289 && (tp2 = peColorStr(p + 13, col2))){
8290 objColor = Tcl_NewListObj(0, NULL);
8291 objPair = Tcl_NewListObj(0, NULL);
8292 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8293 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
8294 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8295 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8296 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8297 strcpy(peED.color.fg, tp2);
8298 p += 13;
8300 else if(strcmp(peED.color.fg, peED.color.fgdef)){
8301 objColor = Tcl_NewListObj(0, NULL);
8302 objPair = Tcl_NewListObj(0, NULL);
8303 Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
8304 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1));
8305 Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
8306 Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
8307 Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
8308 strcpy(peED.color.fg, peED.color.fgdef);
8310 else
8311 peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp);
8313 strcpy(peED.color.bg, tp);
8316 line = p + 11;
8317 break;
8319 case TAG_ITALICON :
8320 peAppListF(peED.interp, lobjp, "%s%s", "italic", "on");
8321 line = p + 1;
8322 break;
8324 case TAG_ITALICOFF :
8325 peAppListF(peED.interp, lobjp, "%s%s", "italic", "off");
8326 line = p + 1;
8327 break;
8329 case TAG_BOLDON :
8330 peAppListF(peED.interp, lobjp, "%s%s", "bold", "on");
8331 line = p + 1;
8332 break;
8334 case TAG_BOLDOFF :
8335 peAppListF(peED.interp, lobjp, "%s%s", "bold", "off");
8336 line = p + 1;
8337 break;
8339 case TAG_ULINEON :
8340 peAppListF(peED.interp, lobjp, "%s%s", "underline", "on");
8341 line = p + 1;
8342 break;
8344 case TAG_ULINEOFF :
8345 peAppListF(peED.interp, lobjp, "%s%s", "underline", "off");
8346 line = p + 1;
8347 break;
8349 case TAG_STRIKEON :
8350 peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on");
8351 line = p + 1;
8352 break;
8354 case TAG_STRIKEOFF :
8355 peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off");
8356 line = p + 1;
8357 break;
8359 case TAG_BIGON :
8360 peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on");
8361 line = p + 1;
8362 break;
8364 case TAG_BIGOFF :
8365 peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off");
8366 line = p + 1;
8367 break;
8369 case TAG_SMALLON :
8370 peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on");
8371 line = p + 1;
8372 break;
8374 case TAG_SMALLOFF :
8375 peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off");
8376 line = p + 1;
8377 break;
8379 case TAG_INVOFF :
8380 case TAG_HANDLEOFF :
8381 if(peED.inhandle){
8382 peAppListF(peED.interp, lobjp, "%s%s", "urlend", "");
8383 peED.inhandle = 0;
8385 /* fall thru and advance "line" */
8387 default :
8388 line = p + 1;
8389 break;
8393 while((p = strindex(line, TAG_EMBED)) != NULL);
8395 if(*line)
8396 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8398 else
8399 peAppListF(peED.interp, lobjp, "%s%s", "t", line);
8401 else
8402 peAppListF(peED.interp, lobjp, "%s%s", "t", "");
8404 if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){
8405 so_truncate(peED.store, 0L);
8406 return(TCL_OK);
8409 return(TCL_ERROR);
8415 * peInterpWritec - collect filtered output, appending to the
8416 * command's result list on each EOL
8419 peNullWritec(int c)
8421 return(1);
8425 char *
8426 peColorStr(char *s, char *b)
8428 int i, j, color;
8430 i = 0;
8431 b[0] = '\0';
8432 while(1){
8433 color = 0;
8434 for(j = 0; j < 3; j++, s++)
8435 if(isdigit((unsigned char) *s))
8436 color = (color * 10) + (*s - '0');
8438 s++; /* advance past ',' */
8439 if(color < 256)
8440 sprintf(b + strlen(b), "%2.2x", color);
8441 else
8442 break;
8444 if(++i == 3)
8445 return(b);
8449 return(NULL);
8454 * returns a list of elements
8457 peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8459 MESSAGECACHE *mc;
8460 HEADER_S h;
8461 int flags, rv = TCL_OK;
8462 long raw;
8463 #if 0
8464 char *color;
8465 #endif
8468 * ONLY full header mode (raw) output should get written to the
8469 * writec function we pass format_header. If there's something
8470 * in the store after formatting ,we'll write it to the Tcl result
8471 * then, not as its accumulated
8473 peED.interp = interp;
8474 peED.obj = Tcl_NewStringObj("", -1);
8476 if(peED.store)
8477 so_seek(peED.store, 0L, 0);
8478 else
8479 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8481 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8483 #if 0
8484 peED.color.fg[0] = '\0';
8485 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8486 peInterpWritec(TAG_EMBED);
8487 peInterpWritec(TAG_FGCOLOR);
8488 gf_puts(color, peInterpWritec);
8489 strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
8492 peED.color.bg[0] = '\0';
8493 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8494 peInterpWritec(TAG_EMBED);
8495 peInterpWritec(TAG_BGCOLOR);
8496 gf_puts(color, peInterpWritec);
8497 strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
8500 peInterpFlush();
8501 #endif
8503 raw = peSequenceNumber(uid);
8504 if(peED.uid != uid){
8505 peED.uid = uid;
8506 peED.body = NULL;
8508 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8509 if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8510 && (mc = mail_elt(wps_global->mail_stream, raw)))){
8511 char buf[256];
8513 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8514 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8516 dprint((1, "ERROR fetching %s of msg %ld: %s",
8517 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8518 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8520 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8521 rv = TCL_ERROR;
8523 else{
8524 zero_atmts(wps_global->atmts);
8525 #ifdef SMIME
8526 if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8527 wps_global->smime->need_passphrase = 0;
8529 fiddle_smime_message(peED.body, raw);
8530 #endif
8531 describe_mime(peED.body, "", 1, 1, 0, flags);
8535 /* NO HANDLES init_handles(&peED.handles);*/
8538 * Collect header pieces into lists via the passed custom formatter. Collect
8539 * everything else in the storage object passed. The latter should only end up
8540 * with raw header data.
8542 * BUG: DEAL WITH COLORS
8544 if(rv == TCL_OK){
8545 HD_INIT(&h, wps_global->VAR_VIEW_HEADERS, wps_global->view_all_except, FE_DEFAULT);
8546 if(format_header(wps_global->mail_stream, raw, NULL, peED.env, &h,
8547 NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){
8548 char buf[256];
8550 snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid));
8551 dprint((1, buf));
8553 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8554 rv = TCL_ERROR;
8558 peInterpFlush();
8559 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj);
8561 so_give(&peED.store);
8562 return(rv);
8565 void
8566 peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_o_t pc, long int which, char *oacs, int flags)
8568 char *p2, buftmp[MAILTMPLEN];
8569 Tcl_Obj *objHdr;
8571 if(!e)
8572 return;
8574 if((which & FE_DATE) && e->date) {
8575 if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8576 snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date);
8577 buftmp[sizeof(buftmp)-1] = '\0';
8578 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf, SIZEOF_20KBUF, buftmp);
8579 peFormatEnvelopeText("Date", p2);
8581 /* BUG: how does error feedback bubble back up? */
8584 if((which & FE_FROM) && e->from)
8585 peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc);
8587 if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from)))
8588 peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc);
8590 if((which & FE_TO) && e->to)
8591 peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc);
8593 if((which & FE_CC) && e->cc)
8594 peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc);
8596 if((which & FE_BCC) && e->bcc)
8597 peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc);
8599 if((which & FE_RETURNPATH) && e->return_path)
8600 peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc);
8602 if((which & FE_NEWSGROUPS) && e->newsgroups)
8603 peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc);
8605 if((which & FE_FOLLOWUPTO) && e->followup_to)
8606 peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc);
8608 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
8609 if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
8610 char *freeme = NULL;
8612 p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000),
8613 (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->subject),
8614 SIZEOF_20KBUF-10000);
8616 if(flags & FM_DISPLAY
8617 && (wps_global->display_keywords_in_subject
8618 || wps_global->display_keywordinits_in_subject)){
8620 /* don't bother if no keywords are defined */
8621 if(some_user_flags_defined(s))
8622 p2 = freeme = prepend_keyword_subject(s, n, p2,
8623 wps_global->display_keywords_in_subject ? KW : KWInit,
8624 NULL, wps_global->VAR_KW_BRACES);
8627 peFormatEnvelopeText("Subject", p2);
8629 if(freeme)
8630 fs_give((void **) &freeme);
8634 if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from)))
8635 peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc);
8637 if((which & FE_MESSAGEID) && e->message_id){
8638 p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
8639 peFormatEnvelopeText("Message-ID", p2);
8642 if((which & FE_INREPLYTO) && e->in_reply_to){
8643 p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
8644 peFormatEnvelopeText("In-Reply-To", p2);
8647 if((which & FE_REFERENCES) && e->references) {
8648 p2 = iutf8ncpy((char *)(wtmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)wtmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
8649 peFormatEnvelopeText("References", p2);
8655 * appends caller's result with: {"text" field_name {field_value}}
8657 void
8658 peFormatEnvelopeText(char *field_name, char *field_value)
8660 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value);
8665 * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }}
8666 * {"rawaddr" field_name {{raw_address} ... }}
8668 void
8669 peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
8670 struct mail_address *addr, int flags, char *oacs, gf_o_t pc)
8672 char *ptmp, *mtmp, *atype = "addr";
8673 int group = 0;
8674 ADDRESS *atmp;
8675 Tcl_Obj *objAddrList = NULL;
8676 STORE_S *tso;
8677 gf_o_t tpc;
8678 extern const char *rspecials;
8679 extern const char *rspecials_minus_quote_and_dot;
8681 if(!addr)
8682 return;
8685 * quickly run down address list to make sure none are patently bogus.
8686 * If so, just blat raw field out.
8688 for(atmp = addr; stream && atmp; atmp = atmp->next)
8689 if(atmp->host && atmp->host[0] == '.'){
8690 char *field, *fields[2];
8692 atype = "rawaddr";
8693 if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL)
8694 return; /* BUG: handle list creation failure */
8696 fields[1] = NULL;
8697 fields[0] = cpystr(field_name);
8698 if((ptmp = strchr(fields[0], ':')) != NULL)
8699 *ptmp = '\0';
8701 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
8702 char *h, *t;
8704 for(t = h = field; *h ; t++)
8705 if(*t == '\015' && *(t+1) == '\012'){
8706 *t = '\0'; /* tie off line */
8708 Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8710 if(!*(h = (++t) + 1)) /* set new h and skip CRLF */
8711 break; /* no more to write */
8713 else if(!*t){ /* shouldn't happen much */
8714 if(h != t)
8715 Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
8717 break;
8720 fs_give((void **)&field);
8723 fs_give((void **)&fields[0]);
8726 if(!objAddrList){
8727 if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL)
8728 return; /* BUG: handle list creation failure */
8730 gf_set_so_writec(&tpc, tso);
8732 while(addr){
8734 atmp = addr->next; /* remember what's next */
8735 addr->next = NULL;
8736 if(!addr->host && addr->mailbox){
8737 mtmp = addr->mailbox;
8738 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
8739 (unsigned char *)wtmp_20k_buf,
8740 SIZEOF_20KBUF, addr->mailbox));
8743 ptmp = addr->personal; /* RFC 1522 personal name? */
8744 addr->personal = iutf8ncpy((char *)wtmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(wtmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
8745 wtmp_20k_buf[10000-1] = '\0';
8748 /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */
8749 if (addr->host) { /* ordinary address? */
8750 if (!(addr->personal || addr->adl)){
8751 so_seek(tso, 0L, 0);
8752 pine_rfc822_address (addr, tpc);
8753 peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8755 else { /* no, must use phrase <route-addr> form */
8756 Tcl_Obj *objTmp;
8758 if (addr->personal){
8759 so_seek(tso, 0L, 0);
8760 pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc);
8761 objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso));
8764 so_seek(tso, 0L, 0);
8765 pine_rfc822_address(addr, tpc);
8766 peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
8769 if(group)
8770 group++;
8772 else if (addr->mailbox) { /* start of group? */
8773 so_seek(tso, 0L, 0);
8774 /* yes, write group name */
8775 pine_rfc822_cat (addr->mailbox, rspecials, tpc);
8776 peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), "");
8777 group = 1; /* in a group */
8779 else if (group) { /* must be end of group (but be paranoid) */
8780 peAppListF(peED.interp, objAddrList, "%s%s", "", ";");
8781 group = 0; /* no longer in that group */
8784 addr->personal = ptmp; /* restore old personal ptr */
8785 if(!addr->host && addr->mailbox){
8786 fs_give((void **)&addr->mailbox);
8787 addr->mailbox = mtmp;
8790 addr->next = atmp;
8791 addr = atmp;
8794 gf_clear_so_writec(tso);
8795 so_give(&tso);
8798 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList);
8803 * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }}
8805 void
8806 peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_o_t pc)
8808 char buf[MAILTMPLEN];
8809 int llen;
8810 char *next_ng;
8811 Tcl_Obj *objNewsgroups;
8813 /* BUG: handle list creation failure */
8814 if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL)
8815 return;
8817 llen = strlen(field_name);
8818 while(*newsgrps){
8819 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++)
8822 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
8823 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
8825 Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1));
8827 newsgrps = next_ng;
8828 if(*newsgrps)
8829 newsgrps++;
8832 peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups);
8837 peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8839 MESSAGECACHE *mc;
8840 ATTACH_S *a;
8841 BODY *body;
8842 Tcl_Obj *objAtt, *tObj, *stObj, *fnObj;
8843 int flags, rv = TCL_OK;
8844 long raw;
8846 peED.interp = interp;
8847 peED.obj = Tcl_GetObjResult(interp);
8849 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER;
8851 raw = peSequenceNumber(uid);
8853 if(peED.uid != uid){
8854 memset(&peED, 0, sizeof(peED));
8856 peED.uid = uid;
8858 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8859 if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8860 && (mc = mail_elt(wps_global->mail_stream, raw)))){
8861 char buf[256];
8863 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8864 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8866 dprint((1, "ERROR fetching %s of msg %ld: %s",
8867 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8868 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8870 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8871 rv = TCL_ERROR;
8873 else{
8874 zero_atmts(wps_global->atmts);
8875 #ifdef SMIME
8876 if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8877 wps_global->smime->need_passphrase = 0;
8879 fiddle_smime_message(peED.body, raw);
8880 #endif
8881 describe_mime(peED.body, "", 1, 1, 0, flags);
8885 /* package up attachment list */
8886 for(a = wps_global->atmts; rv == TCL_OK && a->description != NULL; a++)
8887 if((objAtt = Tcl_NewListObj(0, NULL)) != NULL
8888 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){
8889 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
8891 if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK
8892 && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK
8893 && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK
8894 && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK
8895 && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK
8896 && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK
8897 && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK))
8898 rv = TCL_ERROR;
8900 else
8901 rv = TCL_ERROR;
8903 return(rv);
8908 peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
8910 MESSAGECACHE *mc;
8911 int flags, rv = TCL_OK;
8912 long raw;
8913 char *color;
8915 peED.interp = interp;
8916 peED.obj = Tcl_GetObjResult(interp);
8918 if(peED.store)
8919 so_seek(peED.store, 0L, 0);
8920 else
8921 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
8923 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
8925 if(objc == 1 && objv[0]){ /* flags */
8926 int i, nFlags;
8927 Tcl_Obj **objFlags;
8928 char *flagstr;
8930 Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags);
8931 for(i = 0; i < nFlags; i++){
8932 if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
8933 rv = TCL_ERROR;
8936 if(!strucmp(flagstr, "html"))
8937 flags |= (FM_HTML | FM_HIDESERVER);
8938 else if(!strucmp(flagstr, "images"))
8939 flags |= (FM_HTMLIMAGES);
8943 peED.color.fg[0] = '\0';
8944 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
8945 peInterpWritec(TAG_EMBED);
8946 peInterpWritec(TAG_FGCOLOR);
8947 gf_puts(color, peInterpWritec);
8948 strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
8951 peED.color.bg[0] = '\0';
8952 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
8953 peInterpWritec(TAG_EMBED);
8954 peInterpWritec(TAG_BGCOLOR);
8955 gf_puts(color, peInterpWritec);
8956 strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
8959 peInterpFlush();
8961 init_handles(&peED.handles);
8963 raw = peSequenceNumber(uid);
8965 if(peED.uid != uid){
8966 peED.uid = uid;
8967 peED.body = NULL;
8969 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
8970 if(!((peED.env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &peED.body))
8971 && (mc = mail_elt(wps_global->mail_stream, raw)))){
8972 char buf[256];
8974 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
8975 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
8977 dprint((1, "ERROR fetching %s of msg %ld: %s",
8978 peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
8979 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
8981 Tcl_SetResult(interp, buf, TCL_VOLATILE);
8982 rv = TCL_ERROR;
8984 else{
8985 zero_atmts(wps_global->atmts);
8986 #ifdef SMIME
8987 if(wps_global && wps_global->smime && wps_global->smime->need_passphrase)
8988 wps_global->smime->need_passphrase = 0;
8990 fiddle_smime_message(peED.body, raw);
8991 #endif
8992 describe_mime(peED.body, "", 1, 1, 0, flags);
8996 /* format message body */
8997 if(rv == TCL_OK){
8998 HEADER_S h;
8999 char *errstr;
9001 HD_INIT(&h, wps_global->VAR_VIEW_HEADERS, wps_global->view_all_except, FE_DEFAULT);
9002 #ifdef SMIME
9003 /* kind of a hack, the description maybe shouldn't be in the editorial stuff */
9004 if(wps_global->smime && wps_global->smime->need_passphrase)
9005 flags &= ~FM_NOEDITORIAL;
9006 #endif
9007 if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){
9008 gf_puts(errstr, peInterpWritec);
9009 rv = TCL_ERROR;
9013 peInterpFlush();
9015 so_give(&peED.store);
9016 free_handles(&peED.handles);
9017 return(rv);
9022 peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9024 MESSAGECACHE *mc;
9025 ENVELOPE *env;
9026 BODY *body;
9027 int flags;
9028 long raw;
9029 char *color;
9031 memset(&peED, 0, sizeof(peED));
9032 peED.interp = interp;
9033 peED.obj = Tcl_GetObjResult(interp);
9034 peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
9036 peED.color.fg[0] = '\0';
9037 if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
9038 peInterpWritec(TAG_EMBED);
9039 peInterpWritec(TAG_FGCOLOR);
9040 gf_puts(color, peInterpWritec);
9041 strcpy(peED.color.fgdef, peColorStr(color, wtmp_20k_buf));
9044 peED.color.bg[0] = '\0';
9045 if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
9046 peInterpWritec(TAG_EMBED);
9047 peInterpWritec(TAG_BGCOLOR);
9048 gf_puts(color, peInterpWritec);
9049 strcpy(peED.color.bgdef, peColorStr(color,wtmp_20k_buf));
9052 raw = peSequenceNumber(peED.uid = uid);
9053 body = NULL;
9054 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
9055 if(!((env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &body))
9056 && (mc = mail_elt(wps_global->mail_stream, raw)))){
9057 char buf[256];
9059 snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
9060 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate");
9062 dprint((1, "ERROR fetching %s of msg %ld: %s",
9063 env ? "elt" : "env", mn_get_cur(sp_msgmap(wps_global->mail_stream)),
9064 wps_global->last_error[0] ? wps_global->last_error : "Indeterminate"));
9066 Tcl_SetResult(interp, buf, TCL_VOLATILE);
9067 return(TCL_ERROR);
9070 flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
9072 init_handles(&peED.handles);
9074 (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec);
9076 peInterpFlush();
9078 so_give(&peED.store);
9079 free_handles(&peED.handles);
9080 return(TCL_OK);
9085 * peMessagePartFromCID - return part number assoc'd with given uid and CID
9088 peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9090 char *cid, sect_buf[256];
9091 long raw;
9092 ENVELOPE *env;
9093 BODY *body;
9095 raw = peSequenceNumber(peED.uid = uid);
9096 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
9098 if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){
9099 if((env = pine_mail_fetchstructure(wps_global->mail_stream, raw, &body)) != NULL){
9100 sect_buf[0] = '\0';
9101 if(peLocateBodyByCID(cid, sect_buf, body)){
9102 Tcl_SetResult(interp, sect_buf, TCL_VOLATILE);
9105 else{
9106 Tcl_SetResult(interp, wps_global->last_error[0] ? wps_global->last_error : "Error getting CID", TCL_VOLATILE);
9107 return(TCL_ERROR);
9111 return(TCL_OK);
9116 peLocateBodyByCID(char *cid, char *section, BODY *body)
9118 if(body->type == TYPEMULTIPART){
9119 char subsection[256], *subp;
9120 int n;
9121 PART *part = body->nested.part;
9123 if(!(part = body->nested.part))
9124 return(0);
9126 subp = subsection;
9127 if(section && *section){
9128 for(n = 0;
9129 n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
9132 *subp++ = '.';
9135 n = 1;
9136 do {
9137 sprintf(subp, "%d", n++);
9138 if(peLocateBodyByCID(cid, subsection, &part->body)){
9139 strcpy(section, subsection);
9140 return(1);
9143 while((part = part->next) != NULL);
9145 return(0);
9148 return((body && body->id) ? !strcmp(cid, body->id) : 0);
9153 * peGetFlag - Return 1 or 0 based on requested flags current state
9155 * Params: argv[0] == flagname
9158 peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9160 char *flagname;
9162 Tcl_SetResult(interp,
9163 int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9164 ? peIsFlagged(wps_global->mail_stream, uid, flagname)
9165 : 0),
9166 TCL_VOLATILE);
9167 return(TCL_OK);
9172 peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname)
9174 MESSAGECACHE *mc;
9175 long raw = peSequenceNumber(uid);
9177 if(!((mc = mail_elt(stream, raw)) && mc->valid)){
9178 mail_fetch_flags(stream, ulong2string(uid), FT_UID);
9179 mc = mail_elt(stream, raw);
9182 if(!strucmp(flagname, "deleted"))
9183 return(mc->deleted);
9185 if(!strucmp(flagname, "new"))
9186 return(!mc->seen);
9188 if(!strucmp(flagname, "important"))
9189 return(mc->flagged);
9191 if(!strucmp(flagname, "answered"))
9192 return(mc->answered);
9194 if(!strucmp(flagname, "recent"))
9195 return(mc->recent);
9197 return(0);
9202 * peSetFlag - Set requested flags value to 1 or 0
9204 * Params: abjv[0] == flagname
9205 * objv[1] == newvalue
9208 peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9210 char *flagname, *flagstr = NULL;
9211 int value;
9213 if((flagname = Tcl_GetStringFromObj(objv[0], NULL))
9214 && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){
9215 if(!strucmp(flagname, "deleted")){
9216 flagstr = "\\DELETED";
9218 else if(!strucmp(flagname, "new")){
9219 flagstr = "\\SEEN";
9220 value = !value;
9222 else if(!strucmp(flagname, "important")){
9223 flagstr = "\\FLAGGED";
9225 else if(!strucmp(flagname, "answered")){
9226 flagstr = "\\ANSWERED";
9228 else if(!strucmp(flagname, "recent")){
9229 flagstr = "\\RECENT";
9232 if(flagstr){
9233 wps_global->c_client_error[0] = '\0';
9234 mail_flag(wps_global->mail_stream,
9235 ulong2string(uid),
9236 flagstr, (value ? ST_SET : 0L) | ST_UID);
9237 if(wps_global->c_client_error[0] != '\0'){
9238 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s",
9239 wps_global->c_client_error);
9240 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
9241 return(TCL_ERROR);
9246 Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC);
9247 return(TCL_OK);
9252 * peMsgSelect - Return 1 or 0 based on whether given UID is selected
9254 * Params: argv[0] == selected
9257 peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9259 int value;
9261 if(objc == 1 && objv[0]){
9262 if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){
9263 if(value){
9264 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9265 peMessageNumber(uid), MN_SLCT, 1);
9266 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9267 peMessageNumber(uid), MN_HIDE, 0);
9268 } else {
9269 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9270 peMessageNumber(uid), MN_SLCT, 0);
9271 /* if zoomed, lite hidden bit */
9272 if(any_lflagged(sp_msgmap(wps_global->mail_stream), MN_HIDE))
9273 set_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream),
9274 peMessageNumber(uid), MN_HIDE, 1);
9278 else{
9279 Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC);
9280 return(TCL_ERROR);
9284 Tcl_SetResult(interp,
9285 (get_lflag(wps_global->mail_stream, NULL,
9286 peSequenceNumber(uid),
9287 MN_SLCT))
9288 ? "1" : "0",
9289 TCL_VOLATILE);
9290 return(TCL_OK);
9295 * peAppendIndexParts - append list of digested index pieces to given object
9297 * Params:
9301 peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9303 Tcl_Obj *objField, *objElement, *objp;
9304 ICE_S *h;
9305 IFIELD_S *f;
9306 IELEM_S *ie;
9309 if((h = build_header_work(wps_global, wps_global->mail_stream,
9310 sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9311 gPeITop, gPeICount, fetched)) != NULL){
9312 for(f = h->ifield; f; f = f->next){
9314 if((objField = Tcl_NewListObj(0, NULL)) == NULL)
9315 return(TCL_ERROR);
9317 for(ie = f->ielem; ie ; ie = ie->next){
9319 if((objElement = Tcl_NewListObj(0, NULL)) == NULL)
9320 return(TCL_ERROR);
9322 if(ie->datalen){
9323 /* FIRST: DATA */
9324 #if INTERNAL_INDEX_TRUNCATE
9325 char *ep;
9327 ep = (char *) fs_get((ie->datalen + 1) * sizeof(char));
9328 sprintf(ep, "%.*s", ie->wid, ie->data);
9330 /* and other stuff to pack trunc'd element into a new object */
9331 #endif
9333 objp = Tcl_NewStringObj(ie->data, ie->datalen);
9335 else
9336 objp = Tcl_NewStringObj("", -1);
9338 if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9339 return(TCL_ERROR);
9341 if(ie->color){
9342 Tcl_Obj *objColor;
9343 char hexcolor[32];
9345 if((objp = Tcl_NewListObj(0, NULL)) == NULL)
9346 return(TCL_ERROR);
9348 hex_colorstr(hexcolor, ie->color->fg);
9349 objColor = Tcl_NewStringObj(hexcolor, -1);
9350 if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9351 return(TCL_ERROR);
9353 hex_colorstr(hexcolor, ie->color->bg);
9354 objColor = Tcl_NewStringObj(hexcolor, -1);
9355 if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
9356 return(TCL_ERROR);
9358 else
9359 objp = Tcl_NewStringObj("", -1);
9361 if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9362 return(TCL_ERROR);
9365 * IF we ever want to map the thread characters into nice
9366 * graphical symbols or take advantage of features like clicking
9367 * on a thread element to collapse and such, we need to have
9368 * element tagging. That's what the object creation and append
9369 * are placeholders for
9371 switch(ie->type){
9372 case eThreadInfo :
9373 objp = Tcl_NewStringObj("threadinfo", -1);
9374 break;
9375 case eText :
9376 objp = NULL;
9377 break;
9378 default :
9379 objp = Tcl_NewStringObj(int2string(ie->type), -1);
9380 break;
9383 if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
9384 return(TCL_ERROR);
9386 if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK)
9387 return(TCL_ERROR);
9390 if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){
9391 return(TCL_ERROR);
9396 return(TCL_OK);
9401 * peAppendIndexColor - append index line's foreground/background color
9403 * Params:
9407 peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
9409 char hexfg[32], hexbg[32];
9410 ICE_S *h;
9412 if((h = build_header_work(wps_global, wps_global->mail_stream,
9413 sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9414 gPeITop, gPeICount, fetched))
9415 && h->color_lookup_done
9416 && h->linecolor){
9418 hex_colorstr(hexfg, h->linecolor->fg);
9419 hex_colorstr(hexbg, h->linecolor->bg);
9421 return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg));
9424 return(peAppListF(interp, aObj, "%s", ""));
9429 * peMessageStatusBits - return list flags indicating pine status bits
9431 * Params:
9433 * Returns: list of lists where:
9434 * * the first element is the list of
9435 * field elements data
9436 * * the second element is a two element
9437 * list containing the lines foreground
9438 * and background colors
9441 peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9443 Tcl_SetResult(interp,
9444 peMsgStatBitString(wps_global, wps_global->mail_stream,
9445 sp_msgmap(wps_global->mail_stream), peMessageNumber(uid),
9446 gPeITop, gPeICount, NULL),
9447 TCL_STATIC);
9448 return(TCL_OK);
9452 char *
9453 peMsgStatBitString(struct pine *state,
9454 MAILSTREAM *stream,
9455 MSGNO_S *msgmap,
9456 long msgno,
9457 long top_msgno,
9458 long msgcount,
9459 int *fetched)
9461 static char buf[36];
9462 int i;
9463 long raw;
9464 MESSAGECACHE *mc;
9465 ICE_S *h;
9467 raw = mn_m2raw(msgmap, msgno);
9468 if((h = build_header_work(state, stream, msgmap,
9469 msgno, top_msgno, msgcount, fetched))
9470 && (mc = mail_elt(stream, raw))){
9471 /* return a string representing a bit field where:
9472 index meaning
9473 ----- -------
9474 0 "New"
9475 1 deleted
9476 2 answered
9477 3 flagged
9478 4 to us
9479 5 cc us
9480 6 recent
9481 7 forwarded
9482 8 attachments
9484 i = 0;
9485 buf[i++] = (mc->seen) ? '0' : '1';
9486 buf[i++] = (mc->deleted) ? '1' : '0';
9487 buf[i++] = (mc->answered) ? '1' : '0';
9488 buf[i++] = (mc->flagged) ? '1' : '0';
9489 buf[i++] = (h->to_us) ? '1' : '0';
9490 buf[i++] = (h->cc_us) ? '1' : '0';
9491 buf[i++] = (mc->recent) ? '1' : '0';
9492 buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0';
9493 buf[i++] = '0';
9494 buf[i++] = '\0';
9496 return(buf);
9499 return("100000000");
9503 Tcl_Obj *
9504 peMsgStatNameList(Tcl_Interp *interp,
9505 struct pine *state,
9506 MAILSTREAM *stream,
9507 MSGNO_S *msgmap,
9508 long msgno,
9509 long top_msgno,
9510 long msgcount,
9511 int *fetched)
9513 Tcl_Obj *objList;
9514 long raw;
9515 MESSAGECACHE *mc;
9516 ICE_S *h;
9518 objList = Tcl_NewListObj(0, NULL);
9519 raw = mn_m2raw(msgmap, msgno);
9520 if((h = build_header_work(state, stream, msgmap,
9521 msgno, top_msgno, msgcount, fetched))
9522 && (mc = mail_elt(stream, raw))){
9523 if(!mc->seen)
9524 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1));
9526 if(mc->deleted)
9527 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1));
9529 if(mc->answered)
9530 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1));
9532 if(mc->flagged)
9533 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1));
9535 if(h->to_us)
9536 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1));
9538 if(h->cc_us)
9539 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1));
9541 if(mc->recent)
9542 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1));
9544 if(user_flag_is_set(stream, raw, FORWARDED_FLAG))
9545 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1));
9547 if(get_lflag(wps_global->mail_stream, sp_msgmap(wps_global->mail_stream), msgno, MN_SLCT))
9548 Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1));
9551 return(objList);
9556 * peReplyHeaders - return subject used in reply to given message
9558 * Params:
9560 * Returns: list of header value pairs where headers are:
9561 * In-Reply-To:, Subject:, Cc:
9565 peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9567 long raw;
9568 int flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE;
9569 char *errmsg = NULL, *fcc = NULL, *sect = NULL;
9570 ENVELOPE *env, *outgoing;
9571 BODY *body = NULL;
9572 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
9574 saved_from = (ADDRESS *) NULL;
9575 saved_to = (ADDRESS *) NULL;
9576 saved_cc = (ADDRESS *) NULL;
9577 saved_resent = (ADDRESS *) NULL;
9579 raw = peSequenceNumber(uid);
9581 /* if we're given a valid section number that
9582 * corresponds to a valid msg/rfc822 body part
9583 * then set up headers in attached message.
9585 if(objc == 1 && objv[0]
9586 && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9587 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) sect))
9588 && body->type == TYPEMESSAGE
9589 && !strucmp(body->subtype, "rfc822")){
9590 env = body->nested.msg->env;
9592 else{
9593 sect = NULL;
9594 env = mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9597 if(env){
9598 if(!reply_harvest(wps_global, raw, sect, env,
9599 &saved_from, &saved_to, &saved_cc,
9600 &saved_resent, &flags)){
9602 Tcl_SetResult(interp, "", TCL_STATIC);
9603 return(TCL_ERROR);
9606 outgoing = mail_newenvelope();
9608 reply_seed(wps_global, outgoing, env,
9609 saved_from, saved_to, saved_cc, saved_resent,
9610 &fcc, flags, &errmsg);
9611 if(errmsg){
9612 if(*errmsg){
9613 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
9616 fs_give((void **)&errmsg);
9619 env = pine_mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9621 outgoing->subject = reply_subject(env->subject, NULL, 0);
9622 outgoing->in_reply_to = reply_in_reply_to(env);
9624 err = !(peAppListF(interp, Tcl_GetObjResult(interp),
9625 "%s%a", "to", outgoing->to) == TCL_OK
9626 && peAppListF(interp, Tcl_GetObjResult(interp),
9627 "%s%a", "cc", outgoing->cc) == TCL_OK
9628 && peAppListF(interp, Tcl_GetObjResult(interp),
9629 "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK
9630 && peAppListF(interp, Tcl_GetObjResult(interp),
9631 "%s%s", "subject",
9632 rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
9633 SIZEOF_20KBUF, outgoing->subject)) == TCL_OK
9634 && (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE));
9637 /* Fill in x-reply-uid data and append it */
9638 if(!err && wps_global->mail_stream->uid_validity){
9639 char *prefix = reply_quote_str(env);
9641 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "(%lu %s)(1 %lu %lu)%s",
9642 strlen(prefix), prefix,
9643 wps_global->mail_stream->uid_validity, uid,
9644 wps_global->mail_stream->mailbox);
9646 fs_give((void **) &prefix);
9648 err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9649 "x-reply-uid", wtmp_20k_buf) != TCL_OK;
9652 mail_free_envelope(&outgoing);
9654 if(err)
9655 return(TCL_ERROR);
9657 else
9658 Tcl_SetResult(interp, "", TCL_VOLATILE);
9660 return(TCL_OK);
9666 * peReplyText - return subject used in reply to given message
9668 * Params:
9670 * Returns:
9674 peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9676 long msgno;
9677 char *prefix, *sect = NULL;
9678 int rv = TCL_OK;
9679 ENVELOPE *env;
9680 BODY *body = NULL, *orig_body;
9681 STORE_S *msgtext;
9682 REDRAFT_POS_S *redraft_pos = NULL;
9683 Tcl_Obj *objBody = NULL, *objAttach = NULL;
9685 msgno = peSequenceNumber(uid);
9687 if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9688 Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE);
9689 return(TCL_ERROR);
9692 /*--- Grab current envelope ---*/
9693 /* if we're given a valid section number that
9694 * corresponds to a valid msg/rfc822 body part
9695 * then set up to reply the attached message's
9696 * text.
9698 if(objc == 2 && objv[1]
9699 && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0'
9700 && (body = mail_body(wps_global->mail_stream, msgno, (unsigned char *) sect))
9701 && body->type == TYPEMESSAGE
9702 && !strucmp(body->subtype, "rfc822")){
9703 env = body->nested.msg->env;
9704 orig_body = body->nested.msg->body;
9706 else{
9707 sect = NULL;
9708 env = mail_fetchstructure(wps_global->mail_stream, msgno, &orig_body);
9709 if(!(env && orig_body)){
9710 Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9711 return(TCL_ERROR);
9715 if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
9716 prefix = cpystr(prefix);
9717 else
9718 prefix = reply_quote_str(env);
9721 * BUG? Should there be some way to signal to reply_bddy
9722 * that we'd like it to produced format=flowed body text?
9723 * right now it's hardwired to in pine/reply.c
9726 if((body = reply_body(wps_global->mail_stream, env, orig_body,
9727 msgno, sect, msgtext, prefix,
9728 TRUE, NULL, TRUE, &redraft_pos)) != NULL){
9730 objBody = Tcl_NewListObj(0, NULL);
9732 peSoStrToList(interp, objBody, msgtext);
9734 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9736 /* sniff for attachments */
9737 objAttach = peMsgAttachCollector(interp, body);
9739 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9742 pine_free_body(&body);
9744 else{
9745 Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9746 rv = TCL_ERROR;
9749 fs_give((void **) &prefix);
9751 return(rv);
9756 peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so)
9758 char *sp, *ep;
9759 Tcl_Obj *objp;
9761 for(ep = (char *) so_text(so); *ep; ep++){
9762 sp = ep;
9764 while(*ep && *ep != '\n')
9765 ep++;
9767 objp = Tcl_NewStringObj(sp, ep - sp);
9769 if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK)
9770 return(FALSE);
9773 return(TRUE);
9778 * peForwardHeaders - return subject used in forward of given message
9780 * Params:
9782 * Returns:
9786 peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9789 int result;
9790 long raw;
9791 char *tmp, *sect = NULL;
9792 ENVELOPE *env;
9793 BODY *body;
9795 raw = peSequenceNumber(uid);
9797 /* if we're given a valid section number that
9798 * corresponds to a valid msg/rfc822 body part
9799 * then set up headers in attached message.
9801 if(objc == 1 && objv[0]
9802 && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
9803 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) sect))
9804 && body->type == TYPEMESSAGE
9805 && !strucmp(body->subtype, "rfc822")){
9806 env = body->nested.msg->env;
9808 else{
9809 sect = NULL;
9810 env = mail_fetchstructure(wps_global->mail_stream, raw, NULL);
9813 if(env){
9814 tmp = forward_subject(env, FS_NONE);
9815 result = peAppListF(interp, Tcl_GetObjResult(interp),
9816 "%s%s", "subject", tmp);
9817 fs_give((void **) &tmp);
9819 /* Fill in x-reply-uid data and append it */
9820 if(result == TCL_OK && wps_global->mail_stream->uid_validity){
9821 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s",
9822 wps_global->mail_stream->uid_validity, uid,
9823 wps_global->mail_stream->mailbox);
9824 result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
9825 "x-reply-uid", wtmp_20k_buf) != TCL_OK;
9828 return(result);
9831 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
9832 return(TCL_ERROR);
9838 * peForwardText - return body of message used in
9839 * forward of given message
9841 * Params:
9843 * Returns:
9847 peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9849 long msgno;
9850 char *bodtext, *p, *sect = NULL;
9851 int rv = TCL_OK;
9852 ENVELOPE *env;
9853 BODY *body, *orig_body;
9854 STORE_S *msgtext;
9855 Tcl_Obj *objBody = NULL, *objAttach = NULL;
9857 msgno = peSequenceNumber(uid);
9859 if(objc == 1 && objv[0])
9860 sect = Tcl_GetStringFromObj(objv[0], NULL);
9862 if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
9863 Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE);
9864 return(TCL_ERROR);
9868 if(F_ON(F_FORWARD_AS_ATTACHMENT, wps_global)){
9869 PART **pp;
9870 long totalsize = 0L;
9872 /*---- New Body to start with ----*/
9873 body = mail_newbody();
9874 body->type = TYPEMULTIPART;
9876 /*---- The TEXT part/body ----*/
9877 body->nested.part = mail_newbody_part();
9878 body->nested.part->body.type = TYPETEXT;
9879 body->nested.part->body.contents.text.data = (unsigned char *) msgtext;
9881 pp = &(body->nested.part->next);
9883 /*---- The Message body subparts ----*/
9884 env = pine_mail_fetchstructure(wps_global->mail_stream, msgno, NULL);
9886 if(forward_mime_msg(wps_global->mail_stream, msgno,
9887 (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){
9888 totalsize = (*pp)->body.size.bytes;
9889 pp = &((*pp)->next);
9892 else{
9893 /*--- Grab current envelope ---*/
9894 /* if we're given a valid section number that
9895 * corresponds to a valid msg/rfc822 body part
9896 * then set up to forward the attached message's
9897 * text.
9900 if(sect && *sect != '\0'
9901 && (body = mail_body(wps_global->mail_stream, msgno, (unsigned char *) sect))
9902 && body->type == TYPEMESSAGE
9903 && !strucmp(body->subtype, "rfc822")){
9904 env = body->nested.msg->env;
9905 orig_body = body->nested.msg->body;
9907 else{
9908 sect = NULL;
9909 env = mail_fetchstructure(wps_global->mail_stream, msgno, &orig_body);
9910 if(!(env && orig_body)){
9911 Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
9912 return(TCL_ERROR);
9916 body = forward_body(wps_global->mail_stream, env, orig_body,
9917 msgno, sect, msgtext, FWD_NONE);
9920 if(body){
9921 bodtext = (char *) so_text(msgtext);
9923 objBody = Tcl_NewListObj(0, NULL);
9925 for(p = bodtext; *p; p++){
9926 Tcl_Obj *objp;
9928 bodtext = p;
9929 while(*p && *p != '\n')
9930 p++;
9932 objp = Tcl_NewStringObj(bodtext, p - bodtext);
9934 Tcl_ListObjAppendElement(interp, objBody, objp);
9937 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
9939 /* sniff for attachments */
9940 objAttach = peMsgAttachCollector(interp, body);
9941 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
9943 pine_free_body(&body);
9945 else{
9946 Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
9947 rv = TCL_ERROR;
9950 return(rv);
9956 * peDetach -
9958 * Params: argv[0] == attachment part number
9959 * argv[1] == directory to hold tmp file
9961 * Returns: list containing:
9963 * 0) response: OK or ERROR
9964 * if OK
9965 * 1) attachment's mime type
9966 * 2) attachment's mime sub-type
9967 * 3) attachment's size in bytes (decoded)
9968 * 4) attachment's given file name (if any)
9969 * 5) tmp file holding raw attachment data
9972 peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
9974 char *part, *err, *tfd, *tfn = NULL, *filename;
9975 long raw;
9976 gf_o_t pc;
9977 BODY *body;
9978 STORE_S *store;
9979 Tcl_Obj *rvobj, *tObj, *stObj, *fnObj;
9981 if((part = Tcl_GetStringFromObj(objv[0], NULL))
9982 && (raw = peSequenceNumber(uid))
9983 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) part))){
9985 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
9987 err = NULL;
9988 if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){
9989 tfn = temp_nam(tfd = NULL, "pd");
9991 else if(is_writable_dir(tfd) == 0){
9992 tfn = temp_nam(tfd, "pd");
9994 else
9995 tfn = tfd;
9997 filename = Tcl_GetStringFromObj(fnObj, NULL);
9998 dprint((5, "PEDetach(name: %s, tmpfile: %s)",
9999 filename ? filename : "<null>", tfn));
10001 if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){
10002 gf_set_so_writec(&pc, store);
10003 err = detach(wps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0);
10004 gf_clear_so_writec(store);
10005 so_give(&store);
10007 else
10008 err = "Can't allocate internal storage";
10010 else
10011 err = "Can't get message data";
10013 if(err){
10014 if(tfn)
10015 unlink(tfn);
10017 dprint((1, "PEDetach FAIL: %d: %s", errno, err));
10018 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err);
10019 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
10020 return(TCL_ERROR);
10023 /* package up response */
10024 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10025 Tcl_NewListObj(1, &tObj));
10027 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10028 Tcl_NewListObj(1, &stObj));
10030 rvobj = Tcl_NewLongObj(name_file_size(tfn));
10031 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10032 Tcl_NewListObj(1, &rvobj));
10033 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10034 Tcl_NewListObj(1, &fnObj));
10035 rvobj = Tcl_NewStringObj(tfn, -1);
10036 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10037 Tcl_NewListObj(1, &rvobj));
10039 return(TCL_OK);
10044 * peAttachInfo -
10046 * Params: argv[0] == attachment part number
10048 * Returns: list containing:
10050 * 0) response: OK or ERROR
10051 * if OK
10052 * 1) attachment's mime type
10053 * 2) attachment's mime sub-type
10054 * 3) attachment's size in bytes (decoded)
10055 * 4) attachment's given file name (if any)
10056 * 5) tmp file holding raw attachment data
10059 peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10061 char *part;
10062 long raw;
10063 BODY *body;
10064 PARMLIST_S *plist;
10065 Tcl_Obj *tObj, *stObj, *fnObj;
10067 if((part = Tcl_GetStringFromObj(objv[0], NULL))
10068 && (raw = peSequenceNumber(uid))
10069 && (body = mail_body(wps_global->mail_stream, raw, (unsigned char *) part))){
10071 peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
10073 else{
10074 Tcl_SetResult(interp, "Can't get message data", TCL_STATIC);
10075 return(TCL_ERROR);
10078 /* package up response */
10080 /* filename */
10081 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj);
10083 /* type */
10084 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
10086 /* subtype */
10087 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj);
10089 /* encoding */
10090 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10091 Tcl_NewStringObj((body->encoding < ENCMAX)
10092 ? body_encodings[body->encoding]
10093 : "Unknown", -1));
10095 /* parameters */
10096 if((plist = rfc2231_newparmlist(body->parameter)) != NULL){
10097 Tcl_Obj *lObj = Tcl_NewListObj(0, NULL);
10098 Tcl_Obj *pObj[2];
10100 while(rfc2231_list_params(plist)){
10101 pObj[0] = Tcl_NewStringObj(plist->attrib, -1);
10102 pObj[1] = Tcl_NewStringObj(plist->value, -1);
10103 Tcl_ListObjAppendElement(interp, lObj,
10104 Tcl_NewListObj(2, pObj));
10107 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj);
10108 rfc2231_free_parmlist(&plist);
10111 /* size guesstimate */
10112 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10113 Tcl_NewStringObj(comatose((body->encoding == ENCBASE64)
10114 ? ((body->size.bytes * 3)/4)
10115 : body->size.bytes), -1));
10117 return(TCL_OK);
10122 * peSaveDefault - Default saved file name for the given message
10123 * specified collection/folder
10125 * Params:
10127 * Returns: name of saved message folder or empty string
10131 peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10133 char *folder;
10134 CONTEXT_S *cntxt, *cp;
10135 int colid;
10136 long rawno;
10137 ENVELOPE *env;
10139 if(uid){
10140 if(!(env = pine_mail_fetchstructure(wps_global->mail_stream,
10141 rawno = peSequenceNumber(uid),
10142 NULL))){
10143 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
10144 return(TCL_ERROR);
10147 else
10148 env = NULL;
10150 if(!(folder = save_get_default(wps_global, env, rawno, NULL, &cntxt))){
10151 Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE);
10152 return(TCL_ERROR); /* message expunged! */
10155 for(colid = 0, cp = wps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next)
10158 if(!cp)
10159 colid = 0;
10161 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10162 Tcl_NewIntObj(colid));
10163 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10164 Tcl_NewStringObj(folder, -1));
10165 return(TCL_OK);
10170 * peSaveWork - Save message with given UID in current folder to
10171 * specified collection/folder
10173 * Params: argv[0] == destination context number
10174 * argv[1] == testination foldername
10179 peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags)
10181 int flgs = 0, i, colid;
10182 char *folder, *err = NULL;
10183 CONTEXT_S *cp;
10185 if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){
10186 if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
10187 mn_set_cur(sp_msgmap(wps_global->mail_stream), peMessageNumber(uid));
10188 for(i = 0, cp = wps_global->context_list; cp ; i++, cp = cp->next)
10189 if(i == colid)
10190 break;
10192 if(cp){
10193 if(!READONLY_FOLDER(wps_global->mail_stream)
10194 && (sflags & PSW_COPY) != PSW_COPY
10195 && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, wps_global)))
10196 flgs |= SV_DELETE;
10198 if(colid == 0 && !strucmp(folder, "inbox"))
10199 flgs |= SV_INBOXWOCNTXT;
10201 if(sflags & (PSW_COPY | PSW_MOVE))
10202 flgs |= SV_FIX_DELS;
10204 i = save(wps_global, wps_global->mail_stream,
10205 cp, folder, sp_msgmap(wps_global->mail_stream), flgs);
10207 if(i == mn_total_cur(sp_msgmap(wps_global->mail_stream))){
10208 if(mn_total_cur(sp_msgmap(wps_global->mail_stream)) <= 1L){
10209 if(wps_global->context_list->next
10210 && context_isambig(folder)){
10211 char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders";
10212 snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
10213 "Message %s %s to \"%.15s%s\" in <%.15s%s>",
10214 long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
10215 (sflags & PSW_MOVE) ? "moved" : "copied",
10216 folder,
10217 (strlen(folder) > 15) ? "..." : "",
10218 tag,
10219 (strlen(tag) > 15) ? "..." : "");
10221 else
10222 snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
10223 "Message %s %s to folder \"%.27s%s\"",
10224 long2string(mn_get_cur(sp_msgmap(wps_global->mail_stream))),
10225 (sflags & PSW_MOVE) ? "moved" : "copied",
10226 folder,
10227 (strlen(folder) > 27) ? "..." : "");
10229 else{
10230 /* with mn_set_cur above, this *should not* happen */
10231 Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE);
10232 return(TCL_ERROR);
10235 if(sflags == PSW_NONE && (flgs & SV_DELETE)){
10236 strncat(wtmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(wtmp_20k_buf)-1);
10237 wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
10240 q_status_message(SM_ORDER, 0, 3, wtmp_20k_buf);
10241 return(TCL_OK);
10244 err = wps_global->last_error;
10246 else
10247 err = "open: Unrecognized collection ID";
10249 else
10250 err = "open: Can't read folder";
10252 else
10253 err = "open: Can't get collection ID";
10255 Tcl_SetResult(interp, err, TCL_VOLATILE);
10256 return(TCL_ERROR);
10260 * peSave - Save message with given UID in current folder to
10261 * specified collection/folder
10263 * Params: argv[0] == destination context number
10264 * argv[1] == testination foldername
10266 * NOTE: just a wrapper around peSaveWork
10269 peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10271 return(peSaveWork(interp, uid, objc, objv, PSW_NONE));
10276 * peCopy - Copy message with given UID in current folder to
10277 * specified collection/folder
10279 * Params: argv[0] == destination context number
10280 * argv[1] == testination foldername
10282 * NOTE: just a wrapper around peSaveWork that makes sure
10283 * delete-on-save is NOT set
10286 peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10288 return(peSaveWork(interp, uid, objc, objv, PSW_COPY));
10293 * peMove - Move message with given UID in current folder to
10294 * specified collection/folder
10296 * Params: argv[0] == destination context number
10297 * argv[1] == testination foldername
10299 * NOTE: just a wrapper around peSaveWork that makes sure
10300 * delete-on-save IS set so it can be expunged
10303 peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10305 return(peSaveWork(interp, uid, objc, objv, PSW_MOVE));
10310 * peGotoDefault - Default Goto command file name for the given message
10311 * specified collection/folder
10313 * Params:
10315 * Returns: name of Goto command default folder or empty string
10319 peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv)
10321 char *folder = NULL;
10322 CONTEXT_S *cntxt, *cp;
10323 int colid, inbox;
10325 cntxt = broach_get_folder(wps_global->context_current, &inbox, &folder);
10327 for(colid = 0, cp = wps_global->context_list; cp != cntxt ; colid++, cp = cp->next)
10330 if(!cp)
10331 colid = 0;
10333 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10334 Tcl_NewIntObj(colid));
10335 (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
10336 Tcl_NewStringObj(folder ? folder : "", -1));
10337 return(TCL_OK);
10342 * peReplyQuote -
10344 * Params: argv[0] == attachment part number
10346 * Returns: list containing:
10350 peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
10352 char *quote;
10353 ENVELOPE *env;
10355 if(uid){
10356 if((env = pine_mail_fetchstructure(wps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){
10357 quote = reply_quote_str(env);
10358 Tcl_SetResult(interp, quote, TCL_VOLATILE);
10359 fs_give((void **) &quote);
10361 else{
10362 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
10363 return(TCL_ERROR);
10366 else
10367 Tcl_SetResult(interp, "> ", TCL_VOLATILE);
10369 return(TCL_OK);
10373 void
10374 peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp)
10376 char *ptype = NULL, *psubtype = NULL, *pfile = NULL;
10378 /*------- Figure out suggested file name ----*/
10379 if(body){
10380 if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){
10382 * if part is generic, see if we can get anything
10383 * more from the suggested filename's extension...
10385 if(body->type == TYPEAPPLICATION
10386 && (!body->subtype
10387 || !strucmp(body->subtype, "octet-stream"))){
10388 BODY *fakebody = mail_newbody();
10390 if(set_mime_type_by_extension(fakebody, pfile)){
10391 ptype = body_type_names(fakebody->type);
10392 psubtype = cpystr(fakebody->subtype);
10395 mail_free_body(&fakebody);
10399 if(!ptype) {
10400 ptype = body_type_names(body->type);
10401 psubtype = cpystr(body->subtype
10402 ? body->subtype
10403 : (body->type == TYPETEXT)
10404 ? "plain"
10405 : (body->type == TYPEAPPLICATION)
10406 ? "octet-stream"
10407 : "");
10410 else{
10411 ptype = body_type_names(TYPETEXT);
10412 psubtype = cpystr("plain");
10415 if(extObjp){
10416 *extObjp = Tcl_NewStringObj("", 0);
10418 if(ptype && psubtype && pfile){
10419 size_t l;
10420 char *mtype;
10421 char extbuf[32]; /* mailcap.c limits to three */
10423 l = strlen(ptype) + strlen(psubtype) + 1;
10424 mtype = (char *) fs_get((l+1) * sizeof(char));
10426 snprintf(mtype, l+1, "%s/%s", ptype, psubtype);
10428 if(!set_mime_extension_by_type(extbuf, mtype)){
10429 char *dotp, *p;
10431 for(dotp = NULL, p = pfile; *p; p++)
10432 if(*p == '.')
10433 dotp = p + 1;
10435 if(dotp)
10436 Tcl_SetStringObj(*extObjp, dotp, -1);
10438 else
10439 Tcl_SetStringObj(*extObjp, extbuf, -1);
10441 fs_give((void **) &mtype);
10445 if(tObjp)
10446 *tObjp = Tcl_NewStringObj(ptype, -1);
10448 if(psubtype){
10449 if(stObjp)
10450 *stObjp = Tcl_NewStringObj(psubtype, -1);
10452 fs_give((void **) &psubtype);
10454 else if(stObjp)
10455 *stObjp = Tcl_NewStringObj("", 0);
10457 if(pfile){
10458 if(fnObjp)
10459 *fnObjp = Tcl_NewStringObj(pfile, -1);
10461 fs_give((void **) &pfile);
10463 else if(fnObjp)
10464 *fnObjp = Tcl_NewStringObj("", 0);
10469 * peAppListF - generate a list of elements based on fmt string,
10470 * then append it to the given list object
10474 peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...)
10476 va_list args;
10477 char *p, *sval, nbuf[128];
10478 int ival, err = 0;
10479 unsigned int uval;
10480 long lval;
10481 unsigned long luval;
10482 PATTERN_S *pval;
10483 ADDRESS *aval;
10484 INTVL_S *vval;
10485 Tcl_Obj *lObj = NULL, *sObj;
10487 if((lObj = Tcl_NewListObj(0, NULL)) != NULL){
10488 va_start(args, fmt);
10489 for(p = fmt; *p && !err; p++){
10490 sObj = NULL;
10492 if(*p == '%')
10493 switch(*++p){
10494 case 'i' : /* int value */
10495 ival = va_arg(args, int);
10496 if((sObj = Tcl_NewIntObj(ival)) == NULL)
10497 err++;
10499 break;
10501 case 'u' : /* unsigned int value */
10502 uval = va_arg(args, unsigned int);
10503 snprintf(nbuf, sizeof(nbuf), "%u", uval);
10504 if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10505 err++;
10507 break;
10509 case 'l' : /* long value */
10510 if(*(p+1) == 'u'){
10511 p++;
10512 luval = va_arg(args, unsigned long);
10513 snprintf(nbuf, sizeof(nbuf), "%lu", luval);
10514 if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
10515 err++;
10517 else{
10518 lval = va_arg(args, long);
10519 if((sObj = Tcl_NewLongObj(lval)) == NULL)
10520 err++;
10523 break;
10525 case 's' : /* string value */
10526 sval = va_arg(args, char *);
10527 sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10528 if(sObj == NULL)
10529 err++;
10531 break;
10533 case 'a': /* ADDRESS list */
10534 aval = va_arg(args, ADDRESS *);
10535 if(aval){
10536 char *tmp, *p;
10537 RFC822BUFFER rbuf;
10538 size_t len;
10540 len = est_size(aval);
10541 tmp = (char *) fs_get(len * sizeof(char));
10542 tmp[0] = '\0';
10543 rbuf.f = dummy_soutr;
10544 rbuf.s = NULL;
10545 rbuf.beg = tmp;
10546 rbuf.cur = tmp;
10547 rbuf.end = tmp+len-1;
10548 rfc822_output_address_list(&rbuf, aval, 0L, NULL);
10549 *rbuf.cur = '\0';
10550 p = (char *) rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf, SIZEOF_20KBUF, tmp);
10551 sObj = Tcl_NewStringObj(p, strlen(p));
10552 fs_give((void **) &tmp);
10554 else
10555 sObj = Tcl_NewStringObj("", -1);
10557 break;
10559 case 'p': /* PATTERN_S * */
10560 pval = va_arg(args, PATTERN_S *);
10561 sval = pattern_to_string(pval);
10562 sObj = Tcl_NewStringObj(sval ? sval : "", -1);
10563 break;
10565 case 'v': /* INTVL_S * */
10566 vval = va_arg(args, INTVL_S *);
10567 if(vval){
10568 for(; vval != NULL; vval = vval->next){
10569 peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax);
10572 else
10573 sObj = Tcl_NewListObj(0, NULL);
10575 break;
10577 case 'o': /* Tcl_Obj * */
10578 sObj = va_arg(args, Tcl_Obj *);
10579 break;
10582 if(sObj)
10583 Tcl_ListObjAppendElement(interp, lObj, sObj);
10586 va_end(args);
10589 return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR);
10593 * pePatAppendID - append list of pattern identity variables to given object
10595 void
10596 pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10598 Tcl_Obj *resObj;
10600 resObj = Tcl_NewListObj(0, NULL);
10601 peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick);
10602 peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment);
10603 Tcl_ListObjAppendElement(interp, patObj, resObj);
10608 * pePatAppendPattern - append list of pattern variables to given object
10610 void
10611 pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
10613 ARBHDR_S *ah;
10614 Tcl_Obj *resObj;
10616 resObj = Tcl_NewListObj(0, NULL);
10617 peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to);
10618 peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from);
10619 peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender);
10620 peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc);
10621 peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip);
10622 peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic);
10623 peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news);
10624 peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj);
10625 peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext);
10626 peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext);
10627 peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword);
10628 peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets);
10630 peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score);
10631 peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age);
10632 peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size);
10634 if((ah = pat->patgrp->arbhdr) != NULL){
10635 Tcl_Obj *hlObj, *hObj;
10637 hlObj = Tcl_NewListObj(0, NULL);
10638 Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1));
10640 for(; ah; ah = ah->next){
10641 hObj = Tcl_NewListObj(0, NULL);
10642 peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p);
10643 Tcl_ListObjAppendElement(interp, hlObj, hObj);
10646 Tcl_ListObjAppendElement(interp, resObj, hlObj);
10649 switch(pat->patgrp->fldr_type){
10650 case FLDR_ANY:
10651 peAppListF(interp, resObj, "%s%s", "ftype", "any");
10652 break;
10653 case FLDR_NEWS:
10654 peAppListF(interp, resObj, "%s%s", "ftype", "news");
10655 break;
10656 case FLDR_EMAIL:
10657 peAppListF(interp, resObj, "%s%s", "ftype", "email");
10658 break;
10659 case FLDR_SPECIFIC:
10660 peAppListF(interp, resObj, "%s%s", "ftype", "specific");
10661 break;
10664 peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder);
10665 peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new));
10666 peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec));
10667 peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del));
10668 peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp));
10669 peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans));
10670 peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj));
10671 peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom));
10672 peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy));
10674 Tcl_ListObjAppendElement(interp, patObj, resObj);
10678 char *
10679 pePatStatStr(int value)
10681 switch(value){
10682 case PAT_STAT_EITHER:
10683 return("either");
10684 break;
10686 case PAT_STAT_YES:
10687 return("yes");
10688 break;
10690 default :
10691 return("no");
10692 break;
10698 * peCreateUserContext - create new wps_global and set it up
10700 char *
10701 peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf)
10703 if(wps_global)
10704 peDestroyUserContext(&wps_global);
10706 set_collation(1, 1);
10708 wps_global = new_pine_struct();
10710 /*----------------------------------------------------------------------
10711 Place any necessary constraints on pith processing
10712 ----------------------------------------------------------------------*/
10714 /* got thru close procedure without expunging */
10715 wps_global->noexpunge_on_close = 1;
10717 /* do NOT let user set path to local executable */
10718 wps_global->vars[V_SENDMAIL_PATH].is_user = 0;
10721 /*----------------------------------------------------------------------
10722 Proceed with reading acquiring user settings
10723 ----------------------------------------------------------------------*/
10724 if(wps_global->pinerc)
10725 fs_give((void **) &wps_global->pinerc);
10727 if(wps_global->prc)
10728 free_pinerc_s(&wps_global->prc);
10730 if(!IS_REMOTE(config)){
10731 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config);
10732 return(wtmp_20k_buf);
10735 wps_global->prc = new_pinerc_s(config);
10737 if(defconf){
10738 if(wps_global->pconf)
10739 free_pinerc_s(&wps_global->pconf);
10741 wps_global->pconf = new_pinerc_s(defconf);
10745 * Fake up some user information
10747 wps_global->ui.login = cpystr(user);
10749 #ifdef DEBUG
10751 * Prep for IMAP debugging
10753 setup_imap_debug();
10754 #endif
10756 /* CHECK FOR AND PASS BACK ANY INIT ERRORS */
10757 return(peLoadConfig(wps_global));
10762 void
10763 peDestroyUserContext(struct pine **pps)
10766 completely_done_with_adrbks();
10768 free_pinerc_strings(pps);
10769 #if 0
10770 imap_flush_passwd_cache(TRUE);
10771 #endif
10772 clear_index_cache(sp_inbox_stream(), 0);
10773 free_newsgrp_cache();
10774 mailcap_free();
10775 close_patterns(0L);
10776 free_extra_hdrs();
10777 free_contexts(&wps_global->context_list);
10779 pico_endcolor();
10781 free_strlist(&peCertHosts);
10783 free_pine_struct(pps);
10787 char *
10788 peLoadConfig(struct pine *pine_state)
10790 int rv;
10791 char *s, *db = NULL;
10792 extern void init_signals(void); /* in signal.c */
10794 if(!pine_state)
10795 return("No global state present");
10797 #if 0
10798 /*turned off because we don't care about local user*/
10799 /* need home directory early */
10800 get_user_info(&pine_state->ui);
10802 pine_state->home_dir = cpystr((getenv("HOME") != NULL)
10803 ? getenv("HOME")
10804 : pine_state->ui.homedir);
10805 #endif
10807 init_pinerc(pine_state, &db);
10809 fs_give((void **) &db);
10812 * Initial allocation of array of stream pool pointers.
10813 * We do this before init_vars so that we can re-use streams used for
10814 * remote config files. These sizes may get changed later.
10816 wps_global->s_pool.max_remstream = 2;
10818 wps_global->c_client_error[0] = '\0';
10820 pePrepareForAuthException();
10822 peInitVars(pine_state);
10824 if((s = peAuthException()) != NULL)
10825 return(s);
10826 else if(wps_global->c_client_error[0])
10827 return(wps_global->c_client_error);
10829 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
10830 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
10831 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
10834 * Install callback to handle certificate validation failures,
10835 * allowing the user to continue if they wish.
10837 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery);
10838 mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure);
10841 * Set up a c-client read timeout and timeout handler. In general,
10842 * it shouldn't happen, but a server crash or dead link can cause
10843 * pine to appear wedged if we don't set this up...
10845 mail_parameters(NULL, SET_OPENTIMEOUT,
10846 (void *)((pine_state->VAR_TCPOPENTIMEO
10847 && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
10848 ? (long) rv : 30L));
10849 mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout);
10851 if(pine_state->VAR_RSHOPENTIMEO
10852 && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
10853 mail_parameters(NULL, SET_RSHTIMEOUT, (void *) (long) rv);
10855 if(pine_state->VAR_SSHOPENTIMEO
10856 && ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4))
10857 mail_parameters(NULL, SET_SSHTIMEOUT, (void *) (long) rv);
10860 * Tell c-client not to be so aggressive about uid mappings
10862 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
10865 * Setup referral handling
10867 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
10868 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
10871 * Install extra headers to fetch along with all the other stuff
10872 * mail_fetch_structure and mail_fetch_overview requests.
10874 calc_extra_hdrs();
10876 if(get_extra_hdrs())
10877 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs());
10879 (void) init_username(pine_state);
10881 (void) init_hostname(wps_global);
10883 #ifdef ENABLE_LDAP
10884 (void) init_ldap_pname(wps_global);
10885 #endif /* ENABLE_LDAP */
10887 if(wps_global->prc && wps_global->prc->type == Loc &&
10888 can_access(wps_global->pinerc, ACCESS_EXISTS) == 0 &&
10889 can_access(wps_global->pinerc, EDIT_ACCESS) != 0)
10890 wps_global->readonly_pinerc = 1;
10893 * c-client needs USR2 and we might as well
10894 * do something sensible with HUP and TERM
10896 init_signals();
10898 strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name));
10899 pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0';
10901 init_folders(pine_state); /* digest folder spec's */
10904 * Various options we want to make sure are set OUR way
10906 F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state);
10907 F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state);
10908 F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state);
10911 * Fake screen dimensions for index formatting and
10912 * message display wrap...
10914 wps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo));
10915 wps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH;
10916 wps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH;
10917 if(wps_global->VAR_WP_COLUMNS){
10918 int w = atoi(wps_global->VAR_WP_COLUMNS);
10919 if(w >= 20 && w <= 128)
10920 wps_global->ttyo->screen_cols = w;
10923 wps_global->ttyo->header_rows = 0;
10924 wps_global->ttyo->footer_rows = 0;
10927 /* init colors */
10928 if(wps_global->VAR_NORM_FORE_COLOR)
10929 pico_nfcolor(wps_global->VAR_NORM_FORE_COLOR);
10931 if(wps_global->VAR_NORM_BACK_COLOR)
10932 pico_nbcolor(wps_global->VAR_NORM_BACK_COLOR);
10934 if(wps_global->VAR_REV_FORE_COLOR)
10935 pico_rfcolor(wps_global->VAR_REV_FORE_COLOR);
10937 if(wps_global->VAR_REV_BACK_COLOR)
10938 pico_rbcolor(wps_global->VAR_REV_BACK_COLOR);
10940 pico_set_normal_color();
10942 return(NULL);
10947 peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox)
10949 unsigned long flgs = 0L;
10950 char *s;
10952 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
10954 pePrepareForAuthException();
10956 if(do_inbox)
10957 flgs |= DB_INBOXWOCNTXT;
10959 if(do_broach_folder(mailbox, context, NULL, flgs) && wps_global->mail_stream){
10960 dprint((SYSDBG_INFO, "Mailbox open: %s",
10961 wps_global->mail_stream->mailbox ? wps_global->mail_stream->mailbox : "<UNKNOWN>"));
10962 return(TCL_OK);
10965 Tcl_SetResult(interp,
10966 (s = peAuthException())
10968 : (*wps_global->last_error)
10969 ? wps_global->last_error
10970 : "Login Error",
10971 TCL_VOLATILE);
10972 return(TCL_ERROR);
10976 void
10977 peDestroyStream(struct pine *ps)
10979 int cur_is_inbox;
10981 if(ps){
10982 cur_is_inbox = (sp_inbox_stream() == wps_global->mail_stream);
10984 /* clean up open streams */
10985 if(ps->mail_stream){
10986 expunge_and_close(ps->mail_stream, NULL, EC_NONE);
10987 wps_global->mail_stream = NULL;
10988 wps_global->cur_folder[0] = '\0';
10991 if(ps->msgmap)
10992 mn_give(&ps->msgmap);
10994 if(sp_inbox_stream() && !cur_is_inbox){
10995 ps->mail_stream = sp_inbox_stream();
10996 ps->msgmap = sp_msgmap(ps->mail_stream);
10997 sp_set_expunge_count(wps_global->mail_stream, 0L);
10998 expunge_and_close(sp_inbox_stream(), NULL, EC_NONE);
10999 mn_give(&ps->msgmap);
11006 * pePrepareForAuthException - set globals to get feedback from bowels of c-client
11008 void
11009 pePrepareForAuthException(void)
11011 peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0;
11015 * pePrepareForAuthException - check globals getting feedback from bowels of c-client
11017 char *
11018 peAuthException(void)
11020 static char buf[CRED_REQ_SIZE];
11022 if(peCertQuery){
11023 snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor);
11024 return(buf);
11027 if(peCertFailure){
11028 snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor);
11029 return(buf);
11032 if(peNoPassword){
11033 snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor);
11034 return(buf);
11037 if(peCredentialError){
11038 snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor);
11039 return(buf);
11042 return(NULL);
11046 void
11047 peInitVars(struct pine *ps)
11049 init_vars(ps, NULL);
11052 * fix display/keyboard-character-set to utf-8
11057 if(ps->display_charmap)
11058 fs_give((void **) &ps->display_charmap);
11060 ps->display_charmap = cpystr(WP_INTERNAL_CHARSET);
11062 if(ps->keyboard_charmap)
11063 fs_give((void **) &ps->keyboard_charmap);
11065 ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET);
11067 (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);;
11073 peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
11075 char *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1];
11076 long rawno;
11077 ENVELOPE *env, *outgoing = NULL;
11078 METAENV *metaenv;
11079 PINEFIELD *custom;
11080 BODY *body = NULL;
11082 if(uid){
11083 rawno = peSequenceNumber(uid);
11085 if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){
11086 if(objc == 2 && objv[1]){
11087 subj = Tcl_GetStringFromObj(objv[1], NULL);
11089 else if((env = mail_fetchstructure(wps_global->mail_stream, rawno, NULL)) != NULL){
11090 subj = env->subject;
11092 else{
11093 Tcl_SetResult(interp, wps_global->last_error, TCL_VOLATILE);
11094 return(TCL_ERROR);
11097 if((errstr = bounce_msg_body(wps_global->mail_stream, rawno, NULL,
11098 &to, subj, &outgoing, &body, NULL))){
11099 Tcl_SetResult(interp, errstr, TCL_VOLATILE);
11100 return(TCL_ERROR);
11103 metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11105 if(!outgoing->from)
11106 outgoing->from = generate_from();
11108 rfc822_date(wtmp_20k_buf);
11109 outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
11111 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11112 if(!outgoing->message_id)
11113 outgoing->message_id = generate_message_id(NULL);
11115 /* NO FCC */
11117 if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11118 errstr = errbuf;
11120 pine_free_body(&body);
11122 pine_free_env(&metaenv);
11124 if(custom)
11125 free_customs(custom);
11127 mail_free_envelope(&outgoing);
11128 pine_free_body(&body);
11133 Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE);
11134 return(errstr ? TCL_ERROR : TCL_OK);
11139 peMessageSpamNotice(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
11141 char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
11142 long rawno;
11144 if(uid){
11145 rawno = peSequenceNumber(uid);
11147 if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){
11149 if(objv[1])
11150 subj = Tcl_GetStringFromObj(objv[1], NULL);
11152 rs = peSendSpamReport(rawno, to, subj, errbuf);
11156 Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE);
11157 return(rs ? TCL_ERROR : TCL_OK);
11161 char *
11162 peSendSpamReport(long rawno, char *to, char *subj, char *errbuf)
11164 char *errstr = NULL, *tmp_a_string;
11165 ENVELOPE *env, *outgoing;
11166 METAENV *metaenv;
11167 PINEFIELD *custom;
11168 BODY *body;
11169 static char *fakedomain = "@";
11170 void *msgtext;
11173 if((env = mail_fetchstructure(wps_global->mail_stream, rawno, NULL)) == NULL){
11174 return(wps_global->last_error);
11177 /* empty subject gets "spam" subject */
11178 if(!(subj && *subj))
11179 subj = env->subject;
11181 /*---- New Body to start with ----*/
11182 body = mail_newbody();
11183 body->type = TYPEMULTIPART;
11185 /*---- The TEXT part/body ----*/
11186 body->nested.part = mail_newbody_part();
11187 body->nested.part->body.type = TYPETEXT;
11189 if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
11190 pine_free_body(&body);
11191 return("peSendSpamReport: Can't allocate text");
11193 else{
11194 sprintf(wtmp_20k_buf,
11195 "The attached message is being reported to <%s> as Spam\n",
11196 to);
11197 so_puts((STORE_S *) msgtext, wtmp_20k_buf);
11198 body->nested.part->body.contents.text.data = msgtext;
11201 /*---- Attach the raw message ----*/
11202 if(forward_mime_msg(wps_global->mail_stream, rawno, NULL, env,
11203 &(body->nested.part->next), msgtext)){
11204 outgoing = mail_newenvelope();
11205 metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
11207 else{
11208 pine_free_body(&body);
11209 return("peSendSpamReport: Can't generate forwarded message");
11212 /* rfc822_parse_adrlist feels free to destroy input so copy */
11213 tmp_a_string = cpystr(to);
11214 rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
11215 (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
11216 ? fakedomain : wps_global->maildomain);
11217 fs_give((void **) &tmp_a_string);
11219 outgoing->from = generate_from();
11220 outgoing->subject = cpystr(subj);
11221 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
11222 outgoing->message_id = generate_message_id(NULL);
11224 rfc822_date(wtmp_20k_buf);
11225 outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
11227 /* NO FCC for Spam Reporting */
11229 if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
11230 errstr = errbuf;
11232 pine_free_body(&body);
11234 pine_free_env(&metaenv);
11236 if(custom)
11237 free_customs(custom);
11239 mail_free_envelope(&outgoing);
11240 pine_free_body(&body);
11242 return(errstr);
11246 /* * * * * * * * * * * * * Start of Composer Routines * * * * * * * * * * * */
11250 * PEComposeCmd - export various bits of alpine state
11253 PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11255 char *err = "PECompose: Unknown request";
11257 dprint((2, "PEComposeCmd"));
11259 if(objc == 1){
11260 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11262 else if(!wps_global){
11263 Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC);
11264 return(TCL_ERROR);
11266 else{
11267 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11269 if(s1){
11270 if(!strcmp(s1, "post")){
11271 long flags = PMC_NONE;
11272 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
11273 flags |= PMC_FORCE_QUAL;
11275 return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags));
11277 else if(objc == 2){
11278 if(!strcmp(s1, "userhdrs")){
11279 int i;
11280 char *p;
11281 PINEFIELD *custom, *cp;
11282 ADDRESS *from;
11283 static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL};
11285 custom = peCustomHdrs();
11287 for(i = 0; standard[i]; i++){
11288 p = NULL;
11289 for(cp = custom; cp; cp = cp->next)
11290 if(!strucmp(cp->name, standard[i]))
11291 p = cp->textbuf;
11293 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p);
11296 for(cp = custom; cp != NULL; cp = cp->next){
11297 if(!strucmp(cp->name, "from")){
11298 if(F_OFF(F_ALLOW_CHANGING_FROM, wps_global))
11299 continue;
11301 if(cp->textbuf && strlen(cp->textbuf)){
11302 p = cp->textbuf;
11304 else{
11305 RFC822BUFFER rbuf;
11307 wtmp_20k_buf[0] = '\0';
11308 rbuf.f = dummy_soutr;
11309 rbuf.s = NULL;
11310 rbuf.beg = wtmp_20k_buf;
11311 rbuf.cur = wtmp_20k_buf;
11312 rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
11313 rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL);
11314 *rbuf.cur = '\0';
11315 mail_free_address(&from);
11316 p = wtmp_20k_buf;
11319 else{
11320 p = cp->textbuf;
11321 for(i = 0; standard[i]; i++)
11322 if(!strucmp(standard[i], cp->name))
11323 p = NULL;
11325 if(!p)
11326 continue;
11329 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p);
11332 if(custom)
11333 free_customs(custom);
11335 return(TCL_OK);
11337 else if(!strcmp(s1, "syshdrs")){
11338 int i;
11339 static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL};
11341 for(i = 0; extras[i]; i++)
11342 peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL);
11344 return(TCL_OK);
11346 else if(!strcmp(s1, "composehdrs")){
11347 char **p, *q;
11349 if((p = wps_global->VAR_COMP_HDRS) && *p){
11350 for(; *p; p++)
11351 Tcl_ListObjAppendElement(interp,
11352 Tcl_GetObjResult(interp),
11353 Tcl_NewStringObj(*p, (q = strchr(*p, ':'))
11354 ? (q - *p) : -1));
11356 else
11357 Tcl_SetResult(interp, "", TCL_STATIC);
11359 return(TCL_OK);
11361 else if(!strcmp(s1, "fccdefault")){
11362 int ci = 0;
11363 CONTEXT_S *c = default_save_context(wps_global->context_list), *c2;
11365 for(c2 = wps_global->context_list; c && c != c2; c2 = c2->next)
11366 ci++;
11368 Tcl_ListObjAppendElement(interp,
11369 Tcl_GetObjResult(interp),
11370 Tcl_NewIntObj(ci));
11371 Tcl_ListObjAppendElement(interp,
11372 Tcl_GetObjResult(interp),
11373 Tcl_NewStringObj(wps_global->VAR_DEFAULT_FCC
11374 ? wps_global->VAR_DEFAULT_FCC
11375 : "", -1));
11376 return(TCL_OK);
11378 else if(!strcmp(s1, "noattach")){
11379 peFreeAttach(&peCompAttach);
11380 Tcl_SetResult(interp, "OK", TCL_VOLATILE);
11381 return(TCL_OK);
11383 else if(!strcmp(s1, "from")){
11384 RFC822BUFFER rbuf;
11385 ADDRESS *from = generate_from();
11386 wtmp_20k_buf[0] = '\0';
11387 rbuf.f = dummy_soutr;
11388 rbuf.s = NULL;
11389 rbuf.beg = wtmp_20k_buf;
11390 rbuf.cur = wtmp_20k_buf;
11391 rbuf.end = wtmp_20k_buf+SIZEOF_20KBUF-1;
11392 rfc822_output_address_list(&rbuf, from, 0L, NULL);
11393 *rbuf.cur = '\0';
11394 mail_free_address(&from);
11395 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
11396 return(TCL_OK);
11398 else if(!strcmp(s1, "attachments")){
11399 COMPATT_S *p;
11400 Tcl_Obj *objAttach;
11402 for(p = peCompAttach; p; p = p->next)
11403 if(p->file){
11404 objAttach = Tcl_NewListObj(0, NULL);
11406 /* id */
11407 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11409 /* file name */
11410 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1));
11412 /* file size */
11413 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size));
11415 /* type/subtype */
11416 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype);
11417 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(wtmp_20k_buf,-1));
11419 /* append to list */
11420 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11422 else if(p->body){
11423 char *name;
11425 objAttach = Tcl_NewListObj(0, NULL);
11427 /* id */
11428 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
11430 /* file name */
11431 if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){
11432 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1));
11433 fs_give((void **) &name);
11435 else
11436 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1));
11438 /* file size */
11439 Tcl_ListObjAppendElement(interp, objAttach,
11440 Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64)
11441 ? ((p->l.b.body->size.bytes * 3)/4)
11442 : p->l.b.body->size.bytes));
11444 /* type/subtype */
11445 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11446 body_type_names(p->l.b.body->type),
11447 p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown");
11448 Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(wtmp_20k_buf, -1));
11450 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11453 return(TCL_OK);
11456 else if(objc == 3){
11457 if(!strcmp(s1, "unattach")){
11458 char *id;
11460 if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11461 if(peClearAttachID(id)){
11462 Tcl_SetResult(interp, "OK", TCL_STATIC);
11463 return(TCL_OK);
11465 else
11466 err = "Can't access attachment id";
11468 else
11469 err = "Can't read attachment id";
11471 else if(!strcmp(s1, "attachinfo")){
11472 COMPATT_S *a;
11473 char *id, *s;
11475 /* return: remote-filename size "type/subtype" */
11476 if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
11477 if((a = peGetAttachID(id)) != NULL){
11478 if(a->file){
11479 /* file name */
11480 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1));
11482 /* file size */
11483 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size));
11485 /* type/subtype */
11486 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype);
11487 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf,-1));
11489 /* description */
11490 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11491 Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1));
11492 return(TCL_OK);
11494 else if(a->body){
11495 char *name;
11497 /* file name */
11498 if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){
11499 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1));
11500 fs_give((void **) &name);
11502 else
11503 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1));
11505 /* file size */
11506 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
11507 Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64)
11508 ? ((a->l.b.body->size.bytes * 3)/4)
11509 : a->l.b.body->size.bytes));
11511 /* type/subtype */
11512 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
11513 body_type_names(a->l.b.body->type),
11514 a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown");
11516 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf, -1));
11518 /* description */
11519 if(a->l.b.body->description){
11520 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description);
11522 else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){
11523 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s);
11524 fs_give((void **) &s);
11526 else
11527 wtmp_20k_buf[0] = '\0';
11529 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(wtmp_20k_buf, -1));
11531 return(TCL_OK);
11534 err = "Unknown attachment type";
11536 else
11537 err = "Can't access attachment id";
11539 else
11540 err = "Can't read attachment id";
11543 else if(objc == 7){
11544 if(!strcmp(s1, "attach")){
11545 char *file, *remote, *type, *subtype, *desc;
11547 if((file = Tcl_GetStringFromObj(objv[2], NULL))
11548 && (type = Tcl_GetStringFromObj(objv[3], NULL))
11549 && (subtype = Tcl_GetStringFromObj(objv[4], NULL))
11550 && (remote = Tcl_GetStringFromObj(objv[5], NULL))){
11551 int dl;
11553 desc = Tcl_GetStringFromObj(objv[6], &dl);
11555 if(desc){
11556 Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE);
11557 return(TCL_OK);
11559 else
11560 err = "Can't read file description";
11562 else
11563 err = "Can't read file name";
11569 Tcl_SetResult(interp, err, TCL_STATIC);
11570 return(TCL_ERROR);
11574 COMPATT_S *
11575 peNewAttach(void)
11577 COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S));
11578 memset(p, 0, sizeof(COMPATT_S));
11579 return(p);
11583 void
11584 peFreeAttach(COMPATT_S **a)
11586 if(a && *a){
11587 fs_give((void **) &(*a)->id);
11589 if((*a)->file){
11590 if((*a)->l.f.type)
11591 fs_give((void **) &(*a)->l.f.type);
11593 if((*a)->l.f.subtype)
11594 fs_give((void **) &(*a)->l.f.subtype);
11596 if((*a)->l.f.remote)
11597 fs_give((void **) &(*a)->l.f.remote);
11599 if((*a)->l.f.local){
11600 (void) unlink((*a)->l.f.local);
11601 fs_give((void **) &(*a)->l.f.local);
11604 if((*a)->l.f.description)
11605 fs_give((void **) &(*a)->l.f.description);
11607 else if((*a)->body){
11608 pine_free_body(&(*a)->l.b.body);
11611 peFreeAttach(&(*a)->next);
11612 fs_give((void **) a);
11617 char *
11618 peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl)
11620 COMPATT_S *ap = peNewAttach(), *p;
11621 long hval;
11623 ap->file = TRUE;
11624 ap->l.f.local = cpystr(f);
11625 ap->l.f.size = name_file_size(f);
11627 hval = line_hash(f);
11628 while(1) /* collisions? */
11629 if(peGetAttachID(ap->id = cpystr(long2string(hval)))){
11630 fs_give((void **) &ap->id);
11631 hval += 1;
11633 else
11634 break;
11636 ap->l.f.remote = cpystr(r ? r : "");
11637 ap->l.f.type = cpystr(t ? t : "Text");
11638 ap->l.f.subtype = cpystr(st ? st : "Plain");
11640 ap->l.f.description = fs_get(dl + 1);
11641 snprintf(ap->l.f.description, dl + 1, "%s", d);
11643 if((p = peCompAttach) != NULL){
11645 if(!p->next){
11646 p->next = ap;
11647 break;
11649 while((p = p->next) != NULL);
11651 else
11652 peCompAttach = ap;
11654 return(ap->id);
11658 char *
11659 peBodyAttachID(BODY *b)
11661 COMPATT_S *ap = peNewAttach(), *p;
11662 unsigned long hval;
11664 ap->body = TRUE;
11665 ap->l.b.body = copy_body(NULL, b);
11667 hval = b->id ? line_hash(b->id) : time(0);
11668 while(1) /* collisions? */
11669 if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){
11670 fs_give((void **) &ap->id);
11671 hval += 1;
11673 else
11674 break;
11676 /* move contents pointer to copy */
11677 peBodyMoveContents(b, ap->l.b.body);
11679 if((p = peCompAttach) != NULL){
11681 if(!p->next){
11682 p->next = ap;
11683 break;
11685 while((p = p->next) != NULL);
11687 else
11688 peCompAttach = ap;
11690 return(ap->id);
11694 void
11695 peBodyMoveContents(BODY *bs, BODY *bd)
11697 if(bs && bd){
11698 if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){
11699 PART *ps = bs->nested.part,
11700 *pd = bd->nested.part;
11701 do /* for each part */
11702 peBodyMoveContents(&ps->body, &pd->body);
11703 while ((ps = ps->next) && (pd = pd->next)); /* until done */
11705 else if(bs->contents.text.data){
11706 bd->contents.text.data = bs->contents.text.data;
11707 bs->contents.text.data = NULL;
11714 COMPATT_S *
11715 peGetAttachID(char *h)
11717 COMPATT_S *p;
11719 for(p = peCompAttach; p; p = p->next)
11720 if(!strcmp(p->id, h))
11721 return(p);
11723 return(NULL);
11728 peClearAttachID(char *h)
11730 COMPATT_S *pp, *pt = NULL;
11732 for(pp = peCompAttach; pp; pp = pp->next){
11733 if(!strcmp(pp->id, h)){
11734 if(pt)
11735 pt->next = pp->next;
11736 else
11737 peCompAttach = pp->next;
11739 pp->next = NULL;
11740 peFreeAttach(&pp);
11741 return(TRUE);
11744 pt = pp;
11747 return(FALSE);
11752 * peDoPost - handle preparing header and body text for posting, prepare
11753 * for any Fcc, then call the mailer to send it out
11756 peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
11758 int rv = TCL_OK, recipients;
11759 char *s;
11761 if(commence_fcc(fcc, fcc_cntxtp, TRUE)){
11763 wps_global->c_client_error[0] = wps_global->last_error[0] = '\0';
11764 pePrepareForAuthException();
11766 if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc))
11767 && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){
11768 if((s = peAuthException()) != NULL){
11769 strcpy(errp, s);
11771 else if(wps_global->last_error[0]){
11772 sprintf(errp, "Send Error: %.*s", 64, wps_global->last_error);
11774 else if(wps_global->c_client_error[0]){
11775 sprintf(errp, "Send Error: %.*s", 64, wps_global->c_client_error);
11777 else
11778 strcpy(errp, "Sending Failure");
11780 rv = TCL_ERROR;
11781 dprint((1, "call_mailer failed!"));
11783 else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){
11784 strcpy(errp, "Fcc Failed!. No message saved.");
11785 rv = TCL_ERROR;
11786 dprint((1, "explicit fcc write failed!"));
11788 else{
11789 PINEFIELD *pf;
11790 REPLY_S *reply = NULL;
11792 /* success, now look for x-reply-uid to flip answered flag for? */
11794 for(pf = metaenv->local; pf && pf->name; pf = pf->next)
11795 if(!strucmp(pf->name, "x-reply-uid")){
11796 if(pf->textbuf){
11797 if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){
11799 update_answered_flags(reply);
11801 if(reply->mailbox)
11802 fs_give((void **) &reply->mailbox);
11804 if(reply->prefix)
11805 fs_give((void **) &reply->prefix);
11807 if(reply->data.uid.msgs)
11808 fs_give((void **) &reply->data.uid.msgs);
11810 fs_give((void **) &reply);
11814 break;
11818 else{
11819 dprint((1,"can't open fcc, cont"));
11821 strcpy(errp, "Can't open Fcc");
11822 rv = TCL_ERROR;
11825 return(rv);
11831 * pePostponeCmd - export various bits of alpine state
11834 PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
11836 char *err = "PEPostpone: unknown request";
11837 long uidl;
11838 imapuid_t uid;
11840 dprint((2, "PEPostponeCmd"));
11842 if(objc == 1){
11843 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
11845 else if(!wps_global){
11846 Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC);
11847 return(TCL_ERROR);
11849 else{
11850 char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
11852 if(s1){
11853 if(!strcmp(s1, "extract")){
11854 if(Tcl_GetLongFromObj(interp, objv[2], &uidl) == TCL_OK){
11855 Tcl_Obj *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL;
11856 MAILSTREAM *stream;
11857 BODY *b;
11858 ENVELOPE *env = NULL;
11859 PINEFIELD *custom = NULL, *cp;
11860 REPLY_S *reply = NULL;
11861 ACTION_S *role = NULL;
11862 STORE_S *so;
11863 long n;
11864 int rv = TCL_OK;
11865 char *fcc = NULL, *lcc = NULL;
11866 unsigned flags = REDRAFT_DEL | REDRAFT_PPND;
11868 uid = uidl;
11869 if(objc > 3){ /* optional flags */
11870 int i, nFlags;
11871 Tcl_Obj **objFlags;
11872 char *flagstr;
11874 Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags);
11875 for(i = 0; i < nFlags; i++){
11876 if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
11877 rv = TCL_ERROR;
11880 if(!strucmp(flagstr, "html"))
11881 flags |= REDRAFT_HTML;
11884 /* BUG: should probably complain if argc > 4 */
11886 if(rv == TCL_OK
11887 && postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)
11888 && stream){
11889 if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
11890 if((n = mail_msgno(stream, uid)) > 0L){
11891 if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply,
11892 NULL, &custom, &role, /* should role be NULL? */
11893 flags, so)){
11894 char *charset = NULL;
11896 /* prepare to package up for caller */
11897 objHdr = Tcl_NewListObj(0, NULL);
11899 /* determine body part's charset */
11900 if((charset = parameter_val(b->parameter,"charset")) != NULL){
11901 objOpts = Tcl_NewListObj(0, NULL);
11902 peAppListF(interp, objOpts, "%s%s", "charset", charset);
11903 fs_give((void **) &charset);
11906 /* body part's MIME subtype */
11907 if(b->subtype && strucmp(b->subtype,"plain")){
11908 if(!objOpts)
11909 objOpts = Tcl_NewListObj(0, NULL);
11911 peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype);
11914 peAppListF(interp, objHdr, "%s%a", "from",
11915 role && role->from ? role->from : env->from);
11916 peAppListF(interp, objHdr, "%s%a", "to", env->to);
11917 peAppListF(interp, objHdr, "%s%a", "cc", env->cc);
11918 peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc);
11919 peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to);
11920 peAppListF(interp, objHdr, "%s%s", "subject",
11921 rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
11922 SIZEOF_20KBUF, env->subject));
11924 if(fcc)
11925 peFccAppend(interp, objHdr, fcc, -1);
11927 for(cp = custom; cp && cp->name; cp = cp->next)
11928 switch(cp->type){
11929 case Address :
11930 strncpy(wtmp_20k_buf, cp->name, SIZEOF_20KBUF);
11931 wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11932 peAppListF(interp, objHdr, "%s%a",
11933 lcase((unsigned char *) wtmp_20k_buf), *cp->addr);
11934 break;
11936 case Attachment :
11937 break;
11939 case Fcc :
11940 case Subject :
11941 break; /* ignored */
11943 default :
11944 strncpy(wtmp_20k_buf, cp->name, SIZEOF_20KBUF);
11945 wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11946 peAppListF(interp, objHdr, "%s%s",
11947 lcase((unsigned char *) wtmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val);
11948 break;
11951 if(reply){
11952 /* blat x-Reply-UID: for possible use? */
11953 if(reply->uid){
11954 char uidbuf[MAILTMPLEN], *p;
11955 long i;
11957 for(i = 0L, p = wtmp_20k_buf; reply->data.uid.msgs[i]; i++){
11958 if(i)
11959 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-wtmp_20k_buf));
11961 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-wtmp_20k_buf));
11964 wtmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
11966 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
11967 reply->prefix ? int2string(strlen(reply->prefix))
11968 : (reply->forwarded) ? "" : "0 ",
11969 reply->prefix ? " " : "",
11970 reply->prefix ? reply->prefix : "",
11971 i, reply->data.uid.validity,
11972 wtmp_20k_buf, reply->mailbox);
11974 peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf);
11977 fs_give((void **) &reply->mailbox);
11978 fs_give((void **) &reply->prefix);
11979 fs_give((void **) &reply->data.uid.msgs);
11980 fs_give((void **) &reply);
11983 objBod = Tcl_NewListObj(0, NULL);
11984 peSoStrToList(interp, objBod, so);
11986 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr);
11987 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod);
11989 objAttach = peMsgAttachCollector(interp, b);
11991 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
11993 if(objOpts){
11994 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts);
11997 /* clean up */
11998 if(fcc)
11999 fs_give((void **) &fcc);
12001 if(lcc)
12002 fs_give((void **) &lcc);
12004 mail_free_envelope(&env);
12005 pine_free_body(&b);
12006 free_action(&role);
12008 /* if Drafts got whacked, open INBOX */
12009 if(!wps_global->mail_stream)
12010 do_broach_folder(wps_global->inbox_name,
12011 wps_global->context_list,
12012 NULL, DB_INBOXWOCNTXT);
12014 return(TCL_OK);
12017 so_give(&so);
12019 else
12020 err = "Unknown UID";
12022 else
12023 err = "No internal storage";
12025 /* redraft_work cleaned up the "stream" */
12027 else
12028 err = "No Postponed stream";
12030 else
12031 err = "Malformed extract request";
12033 else if(objc == 2){
12034 if(!strcmp(s1, "any")){
12035 MAILSTREAM *stream;
12037 if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 1) && stream){
12038 Tcl_SetResult(interp, "1", TCL_STATIC);
12040 if(stream != wps_global->mail_stream)
12041 pine_mail_close(stream);
12043 else
12044 Tcl_SetResult(interp, "0", TCL_STATIC);
12046 return(TCL_OK);
12048 else if(!strcmp(s1, "count")){
12049 MAILSTREAM *stream;
12051 if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12052 Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC);
12054 if(stream != wps_global->mail_stream)
12055 pine_mail_close(stream);
12057 else
12058 Tcl_SetResult(interp, "-1", TCL_STATIC);
12060 return(TCL_OK);
12062 else if(!strcmp(s1, "list")){
12063 MAILSTREAM *stream;
12064 ENVELOPE *env;
12065 Tcl_Obj *objEnv = NULL, *objEnvList;
12066 long n;
12068 if(postponed_stream(&stream, wps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
12069 if(!stream->nmsgs){
12070 (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND);
12071 Tcl_SetResult(interp, "", TCL_STATIC);
12072 return(TCL_OK);
12075 objEnvList = Tcl_NewListObj(0, NULL);
12077 for(n = 1; n <= stream->nmsgs; n++){
12078 if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){
12079 objEnv = Tcl_NewListObj(0, NULL);
12081 peAppListF(interp, objEnv, "%s%s", "uid",
12082 ulong2string(mail_uid(stream, n)));
12084 peAppListF(interp, objEnv, "%s%a", "to", env->to);
12086 date_str((char *)env->date, iSDate, 1, wtmp_20k_buf, SIZEOF_20KBUF, 0);
12088 peAppListF(interp, objEnv, "%s%s", "date", wtmp_20k_buf);
12090 peAppListF(interp, objEnv, "%s%s", "subj",
12091 rfc1522_decode_to_utf8((unsigned char *) wtmp_20k_buf,
12092 SIZEOF_20KBUF, env->subject));
12094 Tcl_ListObjAppendElement(interp, objEnvList, objEnv);
12098 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList);
12100 Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
12101 Tcl_NewStringObj("utf-8", -1));
12103 if(stream != wps_global->mail_stream)
12104 pine_mail_close(stream);
12106 else
12107 Tcl_SetResult(interp, "", TCL_STATIC);
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, wps_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 wps_global->expunge_in_progress = 1;
12139 mail_expunge(stream);
12140 wps_global->expunge_in_progress = 0;
12141 if(stream != wps_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(!wps_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 = wtmp_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 = wtmp_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 = wtmp_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,wps_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 = wtmp_20k_buf, "Unknown Post Option: %s", value);
12417 return(peMsgCollected(interp, &md, err, flags));
12420 else{
12421 sprintf(err = wtmp_20k_buf, "Malformed Post Option");
12422 return(peMsgCollected(interp, &md, err, flags));
12425 else {
12426 if(nField != 2){
12427 sprintf(err = wtmp_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 : wps_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 = wtmp_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(wtmp_20k_buf);
12563 md->outgoing->date = (unsigned char *) cpystr(wtmp_20k_buf);
12564 md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from);
12565 md->outgoing->message_id = generate_message_id(NULL);
12567 body = mail_newbody();
12569 /* wire any attachments to body */
12570 if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, wps_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(NULL);
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 = wps_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, wps_global);
12684 F_SET(F_NO_FCC_ATTACH, wps_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, wps_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(wtmp_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(wtmp_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 = (wps_global->context_list && (wps_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(!wps_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 wps_global->c_client_error[0] = '\0';
12889 rfc822_parse_adrlist(&addrlist, addrstr,
12890 (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
12891 ? fakedomain : wps_global->maildomain);
12893 fs_give((void **) &addrstr);
12894 if(wps_global->c_client_error[0]){
12895 Tcl_SetResult(interp, wps_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 wtmp_20k_buf[0] = '\0';
12905 rbuf.f = dummy_soutr;
12906 rbuf.s = NULL;
12907 rbuf.beg = wtmp_20k_buf;
12908 rbuf.cur = wtmp_20k_buf;
12909 rbuf.end = wtmp_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(wtmp_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 wps_global->c_client_error[0] = '\0';
12930 rfc822_parse_adrlist(&addrlist, addrstr,
12931 (F_ON(F_COMPOSE_REJECTS_UNQUAL, wps_global))
12932 ? fakedomain : wps_global->maildomain);
12934 fs_give((void **) &addrstr);
12935 if(wps_global->c_client_error[0]){
12936 Tcl_SetResult(interp, wps_global->c_client_error, TCL_STATIC);
12937 return(TCL_ERROR);
12940 for(atmp = addrlist; atmp; ){
12941 anextp = atmp->next;
12942 atmp->next = NULL;
12943 wtmp_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 = wtmp_20k_buf;
12954 rbuf.cur = wtmp_20k_buf;
12955 rbuf.end = wtmp_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(wtmp_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], wps_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) / wps_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], wps_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 wps_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 wps_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(wps_global->c_client_error[0]){
13689 snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s",
13690 addrs[adri], strlen(addrs[adri]) > 10 ?
13691 "..." : "", wps_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(wps_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 (wps_global->fcc_rule == FCC_RULE_NICK ||
14367 wps_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(wps_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 = wps_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 - wps_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 wps_global->context_list = new_ctxt;
14453 if(!new_ctxt->var.v)
14454 new_ctxt->var = del_ctxt->var;
14456 else if(wps_global->context_list == del_ctxt){
14457 wps_global->context_list = del_ctxt->next;
14458 if(!wps_global->context_list)
14459 return TCL_ERROR; /* this shouldn't happen */
14461 if(wps_global->context_last == del_ctxt)
14462 wps_global->context_last = NULL;
14463 if(wps_global->context_current == del_ctxt){
14464 strncpy(wps_global->cur_folder,
14465 wps_global->mail_stream->mailbox,
14466 sizeof(wps_global->cur_folder));
14467 wps_global->cur_folder[sizeof(wps_global->cur_folder)-1] = '\0';
14468 wps_global->context_current = wps_global->context_list;
14470 del_ctxt->prev = NULL;
14471 del_ctxt->next = NULL;
14472 free_context(&del_ctxt);
14473 init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14474 wps_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 = wps_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 - wps_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 - wps_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 - wps_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 accommodate 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(wps_global->context_list == sh_ctxt)
14566 wps_global->context_list = nsh_ctxt;
14567 init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14568 wps_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 = wps_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 - wps_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 - wps_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 - wps_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(wps_global->context_list == psh_ctxt)
14669 wps_global->context_list = sh_ctxt;
14670 init_inbox_mapping(wps_global->VAR_INBOX_PATH,
14671 wps_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 = wps_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(wps_global->context_list == tmp_ctxt)
14756 wps_global->context_list = new_ctxt;
14757 if(wps_global->context_current == tmp_ctxt){
14758 strncpy(wps_global->cur_folder,
14759 wps_global->mail_stream->mailbox,
14760 sizeof(wps_global->cur_folder));
14761 wps_global->cur_folder[sizeof(wps_global->cur_folder)-1] = '\0';
14762 wps_global->context_current = new_ctxt;
14764 if(wps_global->context_last == tmp_ctxt)
14765 wps_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 = wps_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 - wps_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(wps_global->VAR_INBOX_PATH,
14798 wps_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(wps_global->mail_stream), peMessageNumber(uid));
14828 if(set_up_takeaddr('a', wps_global, sp_msgmap(wps_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(wps_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 *) wtmp_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 = wps_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(wps_global->VAR_LDAP_SERVERS){
15117 for(i = 0; wps_global->VAR_LDAP_SERVERS[i] &&
15118 wps_global->VAR_LDAP_SERVERS[i][0]; i++){
15119 info = break_up_ldap_server(wps_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, ...} {opt, ...}}, ...}}
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);
15301 resObj = Tcl_NewListObj(0, NULL);
15303 if(!strucmp(a, "objectclass")){
15304 if(Tcl_ListObjAppendElement(interp, resObj,
15305 Tcl_NewStringObj("objectclass", -1)) != TCL_OK)
15306 return(TCL_ERROR);
15308 for(tmp = strchr(a, ';'); tmp != NULL; tmp = tmp2){
15309 int retval;
15311 tmp2 = strchr(++tmp, ';');
15312 if(tmp2)
15313 *tmp2 = '\0';
15314 retval = Tcl_ListObjAppendElement(interp, resObj,
15315 Tcl_NewStringObj(tmp, -1));
15316 if(tmp2)
15317 *tmp2 = ';';
15318 if(retval != TCL_OK)
15319 return(TCL_ERROR);
15321 if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15322 return(TCL_ERROR);
15323 if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK)
15324 return(TCL_ERROR);
15326 our_ldap_memfree(a);
15329 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15330 itemObj) != TCL_OK)
15331 return(TCL_ERROR);
15333 fs_give((void **)&winning_e);
15334 return(TCL_OK);
15337 else if(objc == 6){
15338 if(!strcmp(s1, "setaddrs")){
15339 char *listset = Tcl_GetStringFromObj(objv[3], NULL);
15340 char *addrstr = Tcl_GetStringFromObj(objv[4], NULL);
15341 char *tmp, *tmp2, *tmplistset, was_char, *ret_to,
15342 *tmpaddrstr;
15343 int **lset, noreplace = 0;
15344 ADDRESS *adr = NULL, *curadr, *prevadr, *newadr,
15345 *curnewadr, *newadrs;
15346 int curi, i, j, numsrchs, numset, setit;
15347 LDAP_CHOOSE_S *tres;
15348 LDAP_SERV_RES_S *trl;
15349 WPLDAPRES_S *curres;
15350 LDAPMessage *e;
15351 RFC822BUFFER rbuf;
15352 size_t len;
15354 if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){
15355 Tcl_SetResult(interp,
15356 "PELdap results malformed: first arg must be int",
15357 TCL_VOLATILE);
15358 return(TCL_ERROR);
15360 if(listset == NULL || addrstr == NULL) return TCL_ERROR;
15361 tmpaddrstr = cpystr(addrstr);
15363 if(!noreplace){
15364 mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
15365 rfc822_parse_adrlist(&adr, tmpaddrstr, "@");
15366 mail_parameters(NIL, SET_PARSEPHRASE, NULL);
15369 tmplistset = cpystr(listset);
15370 for(curres = wpldap_global->ldap_search_list, numsrchs = 0;
15371 curres; curres = curres->next, numsrchs++);
15372 lset = (int **)fs_get((numsrchs+1)*sizeof(int *));
15373 for(i = 0; i < numsrchs; i++){
15374 for(tmp = tmplistset, numset = 0; *tmp;){
15375 for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15376 if(*tmp2 != '.'){
15377 Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE);
15378 return TCL_ERROR;
15380 if(atoi(tmp) == i) numset++;
15381 tmp2++;
15382 for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15383 if(*tmp2 != ',' && *tmp2 != '\0'){
15384 Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE);
15385 return TCL_ERROR;
15387 if(*tmp2) tmp2++;
15388 tmp = tmp2;
15390 lset[i] = (int *)fs_get((numset+1)*sizeof(int));
15391 for(tmp = tmplistset, j = 0; *tmp && j < numset;){
15392 setit = 0;
15393 for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15394 if(*tmp2 != '.'){
15395 Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE);
15396 return TCL_ERROR;
15398 *tmp2 = '\0';
15399 if(atoi(tmp) == i) setit++;
15400 *tmp2 = '.';
15401 tmp2++;
15402 for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
15403 if(*tmp2 != ',' && *tmp2 != '\0'){
15404 Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE);
15405 return TCL_ERROR;
15407 if(setit){
15408 was_char = *tmp2;
15409 *tmp2 = '\0';
15410 lset[i][j++] = atoi(tmp);
15411 *tmp2 = was_char;
15413 if(*tmp2) tmp2++;
15414 tmp = tmp2;
15416 lset[i][j] = -1;
15418 lset[i] = NULL;
15419 for(i = 0, curres = wpldap_global->ldap_search_list;
15420 i < numsrchs && curres; i++, curres = curres->next){
15421 prevadr = NULL;
15422 for(curadr = adr; curadr; curadr = curadr->next){
15423 if(strcmp(curadr->mailbox, curres->str) == 0
15424 && curadr->host && *curadr->host == '@')
15425 break;
15426 prevadr = curadr;
15428 if(!curadr && !noreplace){
15429 Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE);
15430 return TCL_ERROR;
15432 newadrs = newadr = curnewadr = NULL;
15433 for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){
15434 for(e = ldap_first_entry(trl->ld, trl->res);
15435 e != NULL && lset[i][curi] != -1;
15436 e = ldap_next_entry(trl->ld, e), j++){
15437 if(j == lset[i][curi]){
15438 tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
15439 tres->ld = trl->ld;
15440 tres->selected_entry = e;
15441 tres->info_used = trl->info_used;
15442 tres->serv = trl->serv;
15443 newadr = address_from_ldap(tres);
15444 fs_give((void **)&tres);
15446 if(newadrs == NULL){
15447 newadrs = curnewadr = newadr;
15449 else {
15450 curnewadr->next = newadr;
15451 curnewadr = newadr;
15453 curi++;
15457 if(newadrs == NULL || curnewadr == NULL){
15458 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname");
15459 q_status_message(SM_ORDER, 0, 3, wtmp_20k_buf);
15460 newadr = copyaddr(curadr);
15461 if(newadrs == NULL){
15462 newadrs = curnewadr = newadr;
15464 else {
15465 curnewadr->next = newadr;
15466 curnewadr = newadr;
15469 curnewadr->next = curadr ? curadr->next : NULL;
15470 if(curadr) curadr->next = NULL;
15471 if(curadr == adr)
15472 adr = newadrs;
15473 else{
15474 prevadr->next = newadrs;
15475 if(curadr)
15476 mail_free_address(&curadr);
15480 len = est_size(adr);
15481 ret_to = (char *)fs_get(len * sizeof(char));
15482 ret_to[0] = '\0';
15483 strip_personal_quotes(adr);
15484 rbuf.f = dummy_soutr;
15485 rbuf.s = NULL;
15486 rbuf.beg = ret_to;
15487 rbuf.cur = ret_to;
15488 rbuf.end = ret_to+len-1;
15489 rfc822_output_address_list(&rbuf, adr, 0L, NULL);
15490 *rbuf.cur = '\0';
15491 Tcl_SetResult(interp, ret_to, TCL_VOLATILE);
15492 fs_give((void **)&ret_to);
15493 fs_give((void **)&tmpaddrstr);
15494 fs_give((void **)&tmplistset);
15495 for(i = 0; lset[i]; i++)
15496 fs_give((void **)&lset[i]);
15497 fs_give((void **)&lset);
15498 if(adr)
15499 mail_free_address(&adr);
15501 return(TCL_OK);
15505 #endif /* ENABLE_LDAP */
15506 Tcl_SetResult(interp, err, TCL_STATIC);
15507 return(TCL_ERROR);
15511 #ifdef ENABLE_LDAP
15513 peLdapQueryResults(Tcl_Interp *interp)
15515 WPLDAPRES_S *tsl;
15516 Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
15517 LDAPMessage *e;
15518 LDAP_SERV_RES_S *trl;
15519 /* returned list will be of the form:
15522 * {search-string
15523 * {name, {title, ...}, {unit, ...},
15524 * {org, ...}, {email, ...}},
15525 * ...
15526 * },
15527 * ...
15531 for(tsl = wpldap_global->ldap_search_list;
15532 tsl; tsl = tsl->next){
15533 secObj = Tcl_NewListObj(0, NULL);
15534 if(Tcl_ListObjAppendElement(interp, secObj,
15535 Tcl_NewStringObj(tsl->str ? tsl->str
15536 : "", -1)) != TCL_OK)
15537 return(TCL_ERROR);
15538 resObj = Tcl_NewListObj(0, NULL);
15539 for(trl = tsl->reslist; trl; trl = trl->next){
15540 for(e = ldap_first_entry(trl->ld, trl->res);
15541 e != NULL;
15542 e = ldap_next_entry(trl->ld, e)){
15543 char *dn;
15544 struct berval **cn, **org, **unit, **title, **mail, **sn;
15546 dn = NULL;
15547 cn = org = title = unit = mail = sn = NULL;
15549 itemObj = Tcl_NewListObj(0, NULL);
15550 peLdapEntryParse(trl, e, &cn, &org, &unit, &title,
15551 &mail, &sn);
15552 if(cn){
15553 if(Tcl_ListObjAppendElement(interp, itemObj,
15554 Tcl_NewStringObj(cn[0]->bv_val, -1)) != TCL_OK)
15555 return(TCL_ERROR);
15556 ldap_value_free_len(cn);
15558 else if(sn){
15559 if(Tcl_ListObjAppendElement(interp, itemObj,
15560 Tcl_NewStringObj(sn[0]->bv_val, -1)) != TCL_OK)
15561 return(TCL_ERROR);
15562 ldap_value_free_len(sn);
15564 else{
15565 dn = ldap_get_dn(trl->ld, e);
15567 if(dn && !dn[0]){
15568 our_ldap_dn_memfree(dn);
15569 dn = NULL;
15572 if(Tcl_ListObjAppendElement(interp, itemObj,
15573 Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK)
15574 return(TCL_ERROR);
15576 if(dn)
15577 our_ldap_dn_memfree(dn);
15579 if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR)
15580 return(TCL_ERROR);
15581 if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR)
15582 return(TCL_ERROR);
15583 if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR)
15584 return(TCL_ERROR);
15585 if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR)
15586 return(TCL_ERROR);
15587 if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK)
15588 return(TCL_ERROR);
15589 if(title)
15590 ldap_value_free_len(title);
15591 if(unit)
15592 ldap_value_free_len(unit);
15593 if(org)
15594 ldap_value_free_len(org);
15595 if(mail)
15596 ldap_value_free_len(mail);
15599 if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
15600 return(TCL_ERROR);
15601 if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
15602 secObj) != TCL_OK)
15603 return(TCL_ERROR);
15605 return(TCL_OK);
15609 peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, struct berval **strl)
15611 Tcl_Obj *strlObj;
15612 int i;
15614 strlObj = Tcl_NewListObj(0, NULL);
15615 if(strl){
15616 for(i = 0; ALPINE_LDAP_usable(strl, i); i++){
15617 if(Tcl_ListObjAppendElement(interp, strlObj,
15618 Tcl_NewStringObj(strl[i]->bv_val, -1)) != TCL_OK)
15619 return(TCL_ERROR);
15622 if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK)
15623 return(TCL_ERROR);
15624 return(TCL_OK);
15629 init_ldap_pname(struct pine *ps)
15631 if(!wps_global->VAR_PERSONAL_NAME
15632 || wps_global->VAR_PERSONAL_NAME[0] == '\0'){
15633 char *pname;
15634 struct variable *vtmp;
15636 if(ps->maildomain && *ps->maildomain
15637 && ps->VAR_USER_ID && *ps->VAR_USER_ID){
15638 pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain);
15639 if(pname){
15640 vtmp = &ps->vars[V_PERSONAL_NAME];
15641 if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0')
15642 || (vtmp->is_fixed && !vtmp->fixed_val.p)){
15643 if(vtmp->fixed_val.p)
15644 fs_give((void **)&vtmp->fixed_val.p);
15645 vtmp->fixed_val.p = cpystr(pname);
15647 else {
15648 if(vtmp->global_val.p)
15649 fs_give((void **)&vtmp->global_val.p);
15650 vtmp->global_val.p = cpystr(pname);
15652 fs_give((void **)&pname);
15653 set_current_val(vtmp, FALSE, FALSE);
15657 return 0;
15659 #endif /* ENABLE_LDAP */
15662 * Note: this is taken straight out of pico/composer.c
15664 * strqchr - returns pointer to first non-quote-enclosed occurance of ch in
15665 * the given string. otherwise NULL.
15666 * s -- the string
15667 * ch -- the character we're looking for
15668 * q -- q tells us if we start out inside quotes on entry and is set
15669 * correctly on exit.
15670 * m -- max characters we'll check for ch (set to -1 for no check)
15672 char *
15673 strqchr(char *s, int ch, int *q, int m)
15675 int quoted = (q) ? *q : 0;
15677 for(; s && *s && m != 0; s++, m--){
15678 if(*s == '"'){
15679 quoted = !quoted;
15680 if(q)
15681 *q = quoted;
15684 if(!quoted && *s == ch)
15685 return(s);
15688 return(NULL);
15692 Tcl_Obj *
15693 wp_prune_folders(CONTEXT_S *ctxt,
15694 char *fcc,
15695 int cur_month,
15696 char *type,
15697 unsigned pr,
15698 int *ok,
15699 int moved_fldrs,
15700 Tcl_Interp *interp)
15702 Tcl_Obj *resObj = NULL, *secObj = NULL;
15703 char path2[MAXPATH+1], tmp[21];
15704 int exists, month_to_use;
15705 struct sm_folder *mail_list, *sm;
15707 mail_list = get_mail_list(ctxt, fcc);
15709 for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
15710 if(sm->month_num == cur_month - 1)
15711 break; /* matched a month */
15713 month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
15715 if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){
15716 strncpy(path2, fcc, sizeof(path2)-1);
15717 path2[sizeof(path2)-1] = '\0';
15718 strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1);
15719 tmp[sizeof(tmp)-1] = '\0';
15720 lcase((unsigned char *) tmp);
15721 snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
15723 if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){
15724 (*ok) = 0;
15725 return(NULL);
15727 else if(exists & FEX_ISFILE){
15728 if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){
15729 prune_move_folder(fcc, path2, ctxt);
15730 } else {
15731 resObj = Tcl_NewListObj(0, NULL);
15732 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15733 secObj = Tcl_NewListObj(0, NULL);
15734 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1));
15735 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1));
15736 Tcl_ListObjAppendElement(interp, resObj, secObj);
15740 if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
15741 || pr == PRUNE_NO_AND_ASK){
15742 sm = mail_list;
15743 if(!resObj && sm && sm->name && sm->name[0] != '\0'){
15744 resObj = Tcl_NewListObj(0, NULL);
15745 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
15746 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15748 if(resObj)
15749 secObj = Tcl_NewListObj(0, NULL);
15750 for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){
15751 if(sm->name[0] == '\0') /* can't happen */
15752 continue;
15753 Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1));
15755 if(resObj)
15756 Tcl_ListObjAppendElement(interp, resObj, secObj);
15757 } else if(resObj)
15758 Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
15760 free_folder_list(ctxt);
15762 if((sm = mail_list) != NULL){
15763 while(sm->name){
15764 fs_give((void **)&(sm->name));
15765 sm++;
15768 fs_give((void **)&mail_list);
15771 return(resObj);
15776 hex_colorstr(char *hexcolor, char *str)
15778 char *tstr, *p, *p2, tbuf[256];
15779 int i;
15781 strcpy(hexcolor, "000000");
15782 tstr = color_to_asciirgb(str);
15783 p = tstr;
15784 p2 = strindex(p, ',');
15785 if(p2 == NULL) return 0;
15786 strncpy(tbuf, p, min(50, p2-p));
15787 i = atoi(tbuf);
15788 sprintf(hexcolor, "%2.2x", i);
15789 p = p2+1;
15790 p2 = strindex(p, ',');
15791 if(p2 == NULL) return 0;
15792 strncpy(tbuf, p, min(50, p2-p));
15793 i = atoi(tbuf);
15794 sprintf(hexcolor+2, "%2.2x", i);
15795 p = p2+1;
15796 strncpy(tbuf, p, 50);
15797 i = atoi(tbuf);
15798 sprintf(hexcolor+4, "%2.2x", i);
15800 return 0;
15804 hexval(char ch)
15806 if(ch >= '0' && ch <= '9')
15807 return (ch - '0');
15808 else if (ch >= 'A' && ch <= 'F')
15809 return (10 + (ch - 'A'));
15810 else if (ch >= 'a' && ch <= 'f')
15811 return (10 + (ch - 'a'));
15812 return -1;
15816 ascii_colorstr(char *acolor, char *hexcolor)
15818 int i, hv;
15820 if(strlen(hexcolor) > 6) return 1;
15821 /* red value */
15822 if((hv = hexval(hexcolor[0])) == -1) return 1;
15823 i = 16 * hv;
15824 if((hv = hexval(hexcolor[1])) == -1) return 1;
15825 i += hv;
15826 sprintf(acolor, "%3.3d,", i);
15827 /* green value */
15828 if((hv = hexval(hexcolor[2])) == -1) return 1;
15829 i = 16 * hv;
15830 if((hv = hexval(hexcolor[3])) == -1) return 1;
15831 i += hv;
15832 sprintf(acolor+4, "%3.3d,", i);
15833 /* blue value */
15834 if((hv = hexval(hexcolor[4])) == -1) return 1;
15835 i = 16 * hv;
15836 if((hv = hexval(hexcolor[5])) == -1) return 1;
15837 i += hv;
15838 sprintf(acolor+8, "%3.3d", i);
15840 return 0;
15844 char *
15845 peRandomString(char *b, int l, int f)
15847 static char *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
15848 char *s = b;
15849 int j;
15850 long n;
15852 while(1){
15853 n = random();
15854 for(j = 0; j < ((sizeof(long) * 8) / 5); j++){
15855 if(l-- <= 0){
15856 *s = '\0';
15857 return(b);
15860 switch(f){
15861 case PRS_LOWER_CASE :
15862 *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15863 break;
15865 case PRS_MIXED_CASE :
15866 if(random() % 2){
15867 *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
15868 break;
15871 default :
15872 *s++ = kb[(n & 0x1F)];
15873 break;
15876 n = n >> 5;
15882 long
15883 peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message)
15885 char *t,*t1,tmp[MAILTMPLEN];
15886 unsigned long u;
15887 MESSAGECACHE *elt;
15888 APPEND_PKG *ap = (APPEND_PKG *) data;
15889 *flags = *date = NIL; /* assume no flags or date */
15890 if (ap->flags) fs_give ((void **) &ap->flags);
15891 if (ap->date) fs_give ((void **) &ap->date);
15892 mail_gc (ap->stream,GC_TEXTS);
15893 if (++ap->msgno <= ap->msgmax) {
15894 /* initialize flag string */
15895 memset (t = tmp,0,MAILTMPLEN);
15896 /* output system flags */
15897 if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15898 if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15899 if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15900 if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15901 if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
15902 if ((u = elt->user_flags) != 0L) do /* any user flags? */
15903 if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long)
15904 (2 + strlen
15905 (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) {
15906 if(t-tmp < sizeof(tmp))
15907 *t++ = ' '; /* space delimiter */
15908 strncpy (t,t1,sizeof(tmp)-(t-tmp)); /* copy the user flag */
15910 while (u); /* until no more user flags */
15911 tmp[sizeof(tmp)-1] = '\0';
15912 *flags = ap->flags = cpystr (tmp + 1);
15913 *date = ap->date = cpystr (mail_date (tmp,elt));
15914 *message = ap->message; /* message stringstruct */
15915 INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
15917 else *message = NIL; /* all done */
15918 return LONGT;
15922 /* Initialize file string structure for file stringstruct
15923 * Accepts: string structure
15924 * pointer to message data structure
15925 * size of string
15928 void
15929 ms_init(STRING *s, void *data, unsigned long size)
15931 APPEND_PKG *md = (APPEND_PKG *) data;
15932 s->data = data; /* note stream/msgno and header length */
15933 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT);
15934 mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL);
15935 s->size += s->data1; /* header + body size */
15936 SETPOS (s,0);
15940 /* Get next character from file stringstruct
15941 * Accepts: string structure
15942 * Returns: character, string structure chunk refreshed
15944 char
15945 ms_next(STRING *s)
15947 char c = *s->curpos++; /* get next byte */
15948 SETPOS (s,GETPOS (s)); /* move to next chunk */
15949 return c; /* return the byte */
15953 /* Set string pointer position for file stringstruct
15954 * Accepts: string structure
15955 * new position
15957 void
15958 ms_setpos(STRING *s, unsigned long i)
15960 APPEND_PKG *md = (APPEND_PKG *) s->data;
15961 if (i < s->data1) { /* want header? */
15962 s->chunk = mail_fetchheader (md->stream,md->msgno);
15963 s->chunksize = s->data1; /* header length */
15964 s->offset = 0; /* offset is start of message */
15966 else if (i < s->size) { /* want body */
15967 s->chunk = mail_fetchtext (md->stream,md->msgno);
15968 s->chunksize = s->size - s->data1;
15969 s->offset = s->data1; /* offset is end of header */
15971 else { /* off end of message */
15972 s->chunk = NIL; /* make sure that we crack on this then */
15973 s->chunksize = 1; /* make sure SNX cracks the right way... */
15974 s->offset = i;
15976 /* initial position and size */
15977 s->curpos = s->chunk + (i -= s->offset);
15978 s->cursize = s->chunksize - i;
15983 remote_pinerc_failure(void)
15985 snprintf(wps_global->last_error, sizeof(wps_global->last_error), "%s",
15986 wps_global->c_client_error[0]
15987 ? wps_global->c_client_error
15988 : _("Unable to read remote configuration"));
15990 return(TRUE);
15993 char *
15994 peWebAlpinePrefix(void)
15996 return("Web ");
16000 void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){
16001 char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
16002 *folder = NULL, intro[MAILTMPLEN+1];
16003 long number;
16004 ENVELOPE *e = NULL;
16005 Tcl_Obj *resObj;
16007 if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){
16009 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream)));
16010 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n)));
16012 if(stream){
16013 e = pine_mail_fetchstructure(stream, n, NULL);
16015 if(sp_flagged(stream, SP_INBOX))
16016 folder = NULL;
16017 else{
16018 folder = STREAMNAME(stream);
16019 if(folder[0] == '?' && folder[1] == '\0')
16020 folder = NULL;
16024 format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro));
16026 snprintf(wtmp_20k_buf, SIZEOF_20KBUF,
16027 "%s%s%s%.80s%.80s", intro,
16028 from ? ((number > 1L) ? " Most recent f" : " F") : "",
16029 from ? "rom " : "",
16030 from ? from : "",
16031 subjtext);
16033 Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(wtmp_20k_buf,-1));
16035 Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj);
16040 /* * * * * * * * * RSS 2.0 Support Routines * * * * * * * * * * * */
16043 * PERssCmd - RSS TCL interface
16046 PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
16048 char *s1;
16050 dprint((2, "PERssCmd"));
16052 if(objc == 1){
16053 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
16054 return(TCL_ERROR);
16056 s1 = Tcl_GetStringFromObj(objv[1], NULL);
16058 if(s1){
16059 if(!strcmp(s1, "news")){
16060 return(peRssReturnFeed(interp, "news", wps_global->VAR_RSS_NEWS));
16062 else if(!strcmp(s1, "weather")){
16063 return(peRssReturnFeed(interp, "weather", wps_global->VAR_RSS_WEATHER));
16067 Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC);
16068 return(TCL_ERROR);
16072 * peRssReturnFeed - fetch feed contents and package Tcl response
16075 peRssReturnFeed(Tcl_Interp *interp, char *type, char *link)
16077 RSS_FEED_S *feed;
16078 char *errstr = "UNKNOWN";
16080 if(link){
16081 wps_global->c_client_error[0] = '\0';
16083 if((feed = peRssFeed(interp, type, link)) != NULL)
16084 return(peRssPackageFeed(interp, feed));
16086 if(wps_global->mm_log_error)
16087 errstr = wps_global->c_client_error;
16089 else
16090 errstr = "missing setting";
16092 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr);
16093 Tcl_SetResult(interp, wtmp_20k_buf, TCL_VOLATILE);
16094 return(TCL_ERROR);
16098 * peRssPackageFeed - build a list of feed item elements
16100 * LIST ORDER: {title} {link} {description} {image}
16103 peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed)
16105 RSS_ITEM_S *item;
16107 for(item = feed->items; item; item = item->next)
16108 if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s",
16109 (item->title && *item->title)? item->title : "Feed Provided No Title",
16110 item->link ? item->link : "",
16111 item->description ? item->description : "",
16112 feed->image ? feed->image : "") != TCL_OK)
16113 return(TCL_ERROR);
16115 return(TCL_OK);
16120 * peRssFeed - return cached feed struct or fetch a new one
16122 RSS_FEED_S *
16123 peRssFeed(Tcl_Interp *interp, char *type, char *link)
16125 int i, cache_l, cp_ref;
16126 time_t now = time(0);
16127 RSS_FEED_S *feed = NULL;
16128 RSS_CACHE_S *cache, *cp;
16129 static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE];
16131 if(!strucmp(type,"news")){
16132 cache = &news_cache[0];
16133 cache_l = RSS_NEWS_CACHE_SIZE;
16135 else{
16136 cache = &weather_cache[0];
16137 cache_l = RSS_WEATHER_CACHE_SIZE;
16140 /* search/purge cache */
16141 for(i = 0; i < cache_l; i++)
16142 if(cache[i].link){
16143 if(now > cache[i].stale){
16144 peRssClearCacheEntry(&cache[i]);
16146 else if(!strcmp(link, cache[i].link)){
16147 cache[i].referenced++;
16148 return(cache[i].feed); /* HIT! */
16152 if((feed = peRssFetch(interp, link)) != NULL){
16153 /* find cache slot, and insert feed into cache */
16154 for(i = 0, cp_ref = 0; i < cache_l; i++)
16155 if(!cache[i].feed){
16156 cp = &cache[i];
16157 break;
16159 else if(cache[i].referenced >= cp_ref)
16160 cp = &cache[i];
16162 if(!cp)
16163 cp = &cache[0]; /* failsafe */
16165 peRssClearCacheEntry(cp); /* make sure */
16167 cp->link = cpystr(link);
16168 cp->feed = feed;
16169 cp->referenced = 0;
16170 cp->stale = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60);
16173 return(feed);
16177 * peRssFetch - follow the provided link an return the resulting struct
16179 RSS_FEED_S *
16180 peRssFetch(Tcl_Interp *interp, char *link)
16182 char *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL;
16183 char *buffer = NULL, *bp, *p, *q;
16184 int ttl = 60;
16185 unsigned long port = 0L, buffer_len = 0L;
16186 time_t theirdate = 0;
16187 STORE_S *feed_so = NULL;
16188 TCPSTREAM *tcp_stream;
16190 if(link){
16191 /* grok url */
16192 rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag);
16193 if(scheme && loc && path){
16194 if((p = strchr(loc,':')) != NULL){
16195 *p++ = '\0';
16196 while(*p && isdigit((unsigned char) *p))
16197 port = ((port * 10) + (*p++ - '0'));
16199 if(*p){
16200 Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC);
16201 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16202 return(NULL);
16206 if(scheme && !strucmp(scheme, "feed")){
16207 fs_give((void **) &scheme);
16208 scheme = cpystr("http");
16211 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5);
16212 tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT);
16213 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
16215 if(tcp_stream != NULL){
16216 char rev[128];
16218 snprintf(wtmp_20k_buf, SIZEOF_20KBUF, "GET /%s%s%s%s%s HTTP/1.1\r\nHost: %s\r\nAccept: application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nUser-Agent: Web-Alpine/%s (%s %s)\r\n\r\n",
16219 path, parms ? ":" : "", parms ? parms : "",
16220 query ? "?" : "", query ? query : "", loc,
16221 ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev)));
16223 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5);
16224 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5);
16226 if(tcp_sout(tcp_stream, wtmp_20k_buf, strlen(wtmp_20k_buf))){
16227 int ok = 0, chunked = FALSE;
16229 while((p = tcp_getline(tcp_stream)) != NULL){
16230 if(!ok){
16231 ok++;
16232 if(strucmp(p,"HTTP/1.1 200 OK")){
16233 fs_give((void **) &p);
16234 break; /* bail */
16237 else if(*p == '\0'){ /* first blank line, start of body */
16238 if(buffer || feed_so){
16239 fs_give((void **) &p);
16240 break; /* bail */
16243 if(buffer_len){
16244 buffer = fs_get(buffer_len + 16);
16245 if(!tcp_getbuffer(tcp_stream, buffer_len, buffer))
16246 fs_give((void **) &buffer);
16248 fs_give((void **) &p);
16249 break; /* bail */
16251 else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
16252 fs_give((void **) &p);
16253 break; /* bail */
16256 else if(feed_so){ /* collect body */
16257 if(chunked){
16258 int chunk_len = 0, gotbuf;
16260 /* first line is chunk size in hex */
16261 for(q = p; *q && isxdigit((unsigned char) *q); q++)
16262 chunk_len = (chunk_len * 16) + XDIGIT2C(*q);
16264 if(chunk_len > 0){ /* collect chunk */
16265 char *tbuf = fs_get(chunk_len + 16);
16266 gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf);
16267 if(gotbuf)
16268 so_nputs(feed_so, tbuf, chunk_len);
16270 fs_give((void **) &tbuf);
16272 if(!gotbuf){
16273 fs_give((void **) &p);
16274 break; /* bail */
16278 /* collect trailing CRLF */
16279 gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0');
16280 if(q)
16281 fs_give((void **) &q);
16283 if(chunk_len == 0 || !gotbuf){
16284 fs_give((void **) &p);
16285 break; /* bail */
16288 else
16289 so_puts(feed_so, p);
16291 else{ /* in header, grok fields */
16292 if((q = strchr(p,':')) != NULL){
16293 int l = q - p;
16295 *q++ = '\0';
16296 while(isspace((unsigned char ) *q))
16297 q++;
16299 /* content-length */
16300 if(l == 4 && !strucmp(p, "date")){
16301 theirdate = date_to_local_time_t(q);
16303 else if(l == 7 && !strucmp(p, "expires")){
16304 time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0));
16306 if(expires > 0 && expires < (8 * 60 * 60))
16307 ttl = expires;
16309 else if(l == 12 && !strucmp(p, "content-type")
16310 && struncmp(q,"text/xml", 8)
16311 && struncmp(q,"application/xhtml+xml", 21)
16312 && struncmp(q,"application/rss+xml", 19)
16313 && struncmp(q,"application/xml", 15)){
16314 fs_give((void **) &p);
16315 break; /* bail */
16317 else if(l == 13 && !strucmp(p, "cache-control")){
16318 if(!struncmp(q,"max-age=",8)){
16319 int secs = 0;
16321 for(q += 8; *q && isdigit((unsigned char) *q); q++)
16322 secs = ((secs * 10) + (*q - '0'));
16324 if(secs > 0)
16325 ttl = secs;
16328 else if(l == 14 && !strucmp(p,"content-length")){
16329 while(*q && isdigit((unsigned char) *q))
16330 buffer_len = ((buffer_len * 10) + (*q++ - '0'));
16332 if(*q){
16333 fs_give((void **) &p);
16334 break; /* bail */
16337 else if(l == 17 && !strucmp(p, "transfer-encoding")){
16338 if(!struncmp(q,"chunked", 7)){
16339 chunked = TRUE;
16341 else{ /* unknown encoding */
16342 fs_give((void **) &p);
16343 break; /* bail */
16349 fs_give((void **) &p);
16352 else{
16353 Tcl_SetResult(interp, "RSS send failure", TCL_STATIC);
16354 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16357 tcp_close(tcp_stream);
16358 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
16359 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60);
16360 peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
16362 if(feed_so){
16363 buffer = (char *) so_text(feed_so);
16364 buffer_len = (int) so_tell(feed_so);
16367 if(buffer && buffer_len){
16368 RSS_FEED_S *feed;
16369 char *err;
16370 STORE_S *bucket;
16371 gf_i_t gc;
16372 gf_o_t pc;
16374 /* grok response */
16375 bucket = so_get(CharStar, NULL, EDIT_ACCESS);
16376 gf_set_readc(&gc, buffer, buffer_len, CharStar, 0);
16377 gf_set_so_writec(&pc, bucket);
16378 gf_filter_init();
16379 gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0));
16380 if((err = gf_pipe(gc, pc)) != NULL){
16381 gf_html2plain_rss_free(&feed);
16382 Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16385 so_give(&bucket);
16387 if(feed_so)
16388 so_give(&feed_so);
16389 else
16390 fs_give((void **) &buffer);
16392 return(feed);
16394 else
16395 Tcl_SetResult(interp, "RSS response error", TCL_STATIC);
16397 else
16398 Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
16400 else
16401 Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC);
16403 else
16404 Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC);
16406 return(NULL);
16410 void
16411 peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag)
16413 if(scheme) fs_give((void **) scheme);
16414 if(loc) fs_give((void **) loc);
16415 if(path) fs_give((void **) path);
16416 if(parms) fs_give((void **) parms);
16417 if(query) fs_give((void **) query);
16418 if(frag) fs_give((void **) frag);
16421 void
16422 peRssClearCacheEntry(RSS_CACHE_S *entry)
16424 if(entry){
16425 if(entry->link)
16426 fs_give((void **) &entry->link);
16428 gf_html2plain_rss_free(&entry->feed);
16429 memset(entry, 0, sizeof(RSS_CACHE_S));