1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: keyword.c 203 2006-10-26 17:23:46Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
#include "../pith/headers.h"
19
#include "../pith/keyword.h"
20
#include "../pith/state.h"
21
#include "../pith/flag.h"
22
#include "../pith/string.h"
23
#include "../pith/status.h"
24
#include "../pith/util.h"
33
* Read the keywords array into a KEYWORD_S structure.
34
* Make sure that all of the strings are UTF-8.
37
init_keyword_list(char **keywordarray)
39
char **t, *nickname, *keyword;
40
KEYWORD_S *head = NULL, *new, *kl = NULL;
42
for(t = keywordarray; t && *t && **t; t++){
43
nickname = keyword = NULL;
44
get_pair(*t, &nickname, &keyword, 0, 0);
45
new = new_keyword_s(keyword, nickname);
47
fs_give((void **) &keyword);
50
fs_give((void **) &nickname);
66
new_keyword_s(char *keyword, char *nickname)
70
kw = (KEYWORD_S *) fs_get(sizeof(*kw));
71
memset(kw, 0, sizeof(*kw));
73
if(keyword && *keyword)
74
kw->kw = cpystr(keyword);
76
if(nickname && *nickname)
77
kw->nick = cpystr(nickname);
84
free_keyword_list(KEYWORD_S **kl)
88
free_keyword_list(&(*kl)->next);
91
fs_give((void **) &(*kl)->kw);
94
fs_give((void **) &(*kl)->nick);
96
fs_give((void **) kl);
102
* Return a pointer to the keyword associated with a nickname, or the
103
* input itself if no match.
106
nick_to_keyword(char *nick)
112
for(kw = ps_global->keywords; kw; kw = kw->next)
113
if(!strcmp(nick, kw->nick ? kw->nick : kw->kw ? kw->kw : "")){
125
* Return a pointer to the nickname associated with a keyword, or the
126
* input itself if no match.
129
keyword_to_nick(char *keyword)
135
for(kw = ps_global->keywords; kw; kw = kw->next)
136
if(!strcmp(keyword, kw->kw ? kw->kw : "")){
148
user_flag_is_set(MAILSTREAM *stream, long unsigned int rawno, char *keyword)
154
if(rawno > 0L && stream
155
&& rawno <= stream->nmsgs
156
&& (mc = mail_elt(stream, rawno)) != NULL){
157
j = user_flag_index(stream, keyword);
158
if(j >= 0 && j < NUSERFLAGS && ((1 << j) & mc->user_flags))
168
* Returns the bit position of the keyword in stream, else -1.
171
user_flag_index(MAILSTREAM *stream, char *keyword)
175
if(stream && keyword){
176
for(i = 0; i < NUSERFLAGS; i++)
177
if(stream->user_flags[i] && !strucmp(keyword, stream->user_flags[i])){
187
/*----------------------------------------------------------------------
188
Build flags string based on requested flags and what's set in messagecache
190
Args: mc -- message cache element to dig the flags out of
191
flags -- flags to test
192
flagbuf -- place to write string representation of bits
194
Result: flags represented in bits and mask written in flagbuf
197
flag_string(MESSAGECACHE *mc, long int flags, char *flagbuf, size_t flagbuflen)
202
*(p = flagbuf) = '\0';
207
if((flags & F_DEL) && mc->deleted)
208
sstrncpy(&p, "\\DELETED ", flagbuflen-(p-flagbuf));
210
if((flags & F_ANS) && mc->answered)
211
sstrncpy(&p, "\\ANSWERED ", flagbuflen-(p-flagbuf));
213
if((flags & F_FLAG) && mc->flagged)
214
sstrncpy(&p, "\\FLAGGED ", flagbuflen-(p-flagbuf));
216
if((flags & F_SEEN) && mc->seen)
217
sstrncpy(&p, "\\SEEN ", flagbuflen-(p-flagbuf));
219
if(p != flagbuf && (flagbuflen-(p-flagbuf)>0))
225
* Find the last message in dstn_stream's folder that has message_id
226
* equal to the argument. Set its keywords equal to the keywords which
227
* are set in message mc from stream kw_stream.
229
* If you just saved the message you're looking for, it is a good idea
230
* to send a ping over before you call this routine.
232
* Args: kw_stream -- stream containing message mc
233
* mcsrc -- mail_elt for the source message
234
* dstn_stream -- where the new message is
235
* message_id -- the message id of the new message
236
* guess -- this is a positive integer, for example, 10. If it
237
* is set we will first try to find the message_id
238
* within the last "guess" messages in the folder,
239
* unless the whole folder isn't much bigger than that
242
set_keywords_in_msgid_msg(MAILSTREAM *kw_stream, MESSAGECACHE *mcsrc,
243
MAILSTREAM *dstn_stream, char *message_id, long int guess)
245
SEARCHPGM *pgm = NULL;
249
extern MAILSTREAM *mm_search_stream;
250
extern long mm_search_count;
252
if(!(kw_stream && dstn_stream))
255
mm_search_count = 0L;
256
mm_search_stream = dstn_stream;
257
while(mm_search_count == 0L && iter++ < 2
258
&& (pgm = mail_newsearchpgm()) != NULL){
259
pgm->message_id = mail_newstringlist();
260
pgm->message_id->text.data = (unsigned char *) cpystr(message_id);
261
pgm->message_id->text.size = strlen(message_id);
264
/* lots of messages? restrict to last guess message on first try */
265
if(dstn_stream->nmsgs > guess + 40L){
266
pgm->msgno = mail_newsearchset();
267
pgm->msgno->first = dstn_stream->nmsgs - guess;
268
pgm->msgno->first = MIN(MAX(pgm->msgno->first, 1),
270
pgm->msgno->last = dstn_stream->nmsgs;
276
pine_mail_search_full(dstn_stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
278
for(newmsgno=dstn_stream->nmsgs; newmsgno > 0L; newmsgno--)
279
if((mc = mail_elt(dstn_stream, newmsgno)) && mc->searched)
283
for(k = 0; k < NUSERFLAGS; k++)
284
if(mcsrc && mcsrc->user_flags & (1 << k)
285
&& kw_stream->user_flags[k]
286
&& kw_stream->user_flags[k][0]){
290
* Check to see if we know it is impossible to set
291
* this keyword before we try.
293
if(dstn_stream->kwd_create ||
294
(((i = user_flag_index(dstn_stream,
295
kw_stream->user_flags[k])) >= 0)
297
mail_flag(dstn_stream, long2string(newmsgno),
298
kw_stream->user_flags[k], ST_SET);
301
int some_defined = 0, w;
302
static time_t last_status_message = 0;
304
char b[200], c[200], *p;
306
for(i = 0; !some_defined && i < NUSERFLAGS; i++)
307
if(dstn_stream->user_flags[i])
311
* Some unusual status message handling here. We'd
312
* like to print out one status message for every
313
* keyword missed, but if that happens every time
314
* you save to a particular folder, that would get
315
* annoying. So we only print out the first for each
316
* save or aggregate save.
318
if((now=time((time_t *) 0)) - last_status_message > 3){
319
last_status_message = now;
321
snprintf(b, sizeof(b), "Losing keyword \"%.30s\". No more keywords allowed in ", keyword_to_nick(kw_stream->user_flags[k]));
322
w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(c)-1);
323
p = short_str(STREAMNAME(dstn_stream), c, sizeof(c), w,
325
q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
328
snprintf(b, sizeof(b), "Losing keyword \"%.30s\". Can't add keywords in ", keyword_to_nick(kw_stream->user_flags[k]));
329
w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(b)-1);
330
p = short_str(STREAMNAME(dstn_stream), c, sizeof(c), w,
332
q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
337
dprint((1, "Losing keyword \"%s\". No more keywords allowed in %s\n", kw_stream->user_flags[k], dstn_stream->mailbox ? dstn_stream->mailbox : "target folder"));
340
dprint((1, "Losing keyword \"%s\". Can't add keywords in %s\n", kw_stream->user_flags[k], dstn_stream->mailbox ? dstn_stream->mailbox : "target folder"));
350
get_msgno_by_msg_id(MAILSTREAM *stream, char *message_id, MSGNO_S *msgmap)
352
SEARCHPGM *pgm = NULL;
353
long hint = mn_m2raw(msgmap, mn_get_cur(msgmap));
357
extern MAILSTREAM *mm_search_stream;
358
extern long mm_search_count;
360
if(!(message_id && message_id[0]))
363
mm_search_count = 0L;
364
mm_search_stream = stream;
365
while(mm_search_count == 0L && iter++ < 3
366
&& (pgm = mail_newsearchpgm()) != NULL){
367
pgm->message_id = mail_newstringlist();
368
pgm->message_id->text.data = (unsigned char *) cpystr(message_id);
369
pgm->message_id->text.size = strlen(message_id);
371
if(iter > 1 || hint > stream->nmsgs)
375
/* restrict to hint message on first try */
376
pgm->msgno = mail_newsearchset();
377
pgm->msgno->first = pgm->msgno->last = hint;
380
/* restrict to last 50 messages on 2nd try */
381
pgm->msgno = mail_newsearchset();
382
if(stream->nmsgs > 100L)
383
pgm->msgno->first = stream->nmsgs-50L;
385
pgm->msgno->first = 1L;
389
pgm->msgno->last = stream->nmsgs;
392
pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
395
for(newmsgno=stream->nmsgs; newmsgno > 0L; newmsgno--)
396
if((mc = mail_elt(stream, newmsgno)) && mc->searched)
401
return(mn_raw2m(msgmap, newmsgno));
406
* These chars are not allowed in keywords.
408
* Returns 0 if ok, 1 if not.
409
* Returns an allocated error message on error.
412
keyword_check(char *kw, char **error)
420
kw = nick_to_keyword(kw);
422
if((t = strindex(kw, SPACE)) ||
423
(t = strindex(kw, '{')) ||
424
(t = strindex(kw, '(')) ||
425
(t = strindex(kw, ')')) ||
426
(t = strindex(kw, ']')) ||
427
(t = strindex(kw, '%')) ||
428
(t = strindex(kw, '"')) ||
429
(t = strindex(kw, '\\')) ||
430
(t = strindex(kw, '*'))){
437
snprintf(buf, sizeof(buf), "%s not allowed in keywords",
445
*error = cpystr(buf);