2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/keyword.h"
17 #include "../pith/state.h"
18 #include "../pith/flag.h"
19 #include "../pith/string.h"
20 #include "../pith/status.h"
21 #include "../pith/util.h"
30 * Read the keywords array into a KEYWORD_S structure.
31 * Make sure that all of the strings are UTF-8.
34 init_keyword_list(char **keywordarray
)
36 char **t
, *nickname
, *keyword
;
37 KEYWORD_S
*head
= NULL
, **tail
;
40 for(t
= keywordarray
; t
&& *t
&& **t
; t
++){
41 nickname
= keyword
= NULL
;
42 get_pair(*t
, &nickname
, &keyword
, 0, 0);
43 *tail
= new_keyword_s(keyword
, nickname
);
44 tail
= &(*tail
)->next
;
47 fs_give((void **) &keyword
);
50 fs_give((void **) &nickname
);
58 new_keyword_s(char *keyword
, char *nickname
)
62 kw
= (KEYWORD_S
*) fs_get(sizeof(*kw
));
63 memset(kw
, 0, sizeof(*kw
));
65 if(keyword
&& *keyword
)
66 kw
->kw
= cpystr(keyword
);
68 if(nickname
&& *nickname
)
69 kw
->nick
= cpystr(nickname
);
76 free_keyword_list(KEYWORD_S
**kl
)
80 free_keyword_list(&(*kl
)->next
);
83 fs_give((void **) &(*kl
)->kw
);
86 fs_give((void **) &(*kl
)->nick
);
88 fs_give((void **) kl
);
94 * Return a pointer to the keyword associated with a nickname, or the
95 * input itself if no match.
98 nick_to_keyword(char *nick
)
104 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
105 if(!strcmp(nick
, kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "")){
117 * Return a pointer to the nickname associated with a keyword, or the
118 * input itself if no match.
121 keyword_to_nick(char *keyword
)
127 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
128 if(!strcmp(keyword
, kw
->kw
? kw
->kw
: "")){
140 * Return a pointer to the keyword associated with an initial, or the
141 * input itself if no match.
142 * Only works for ascii initials.
145 initial_to_keyword(char *initial
)
153 if(initial
[0] && initial
[1] == '\0'){
155 if(isascii(init
) && isupper(init
))
156 init
= tolower((unsigned char) init
);
158 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
160 kwinit
= kw
->nick
[0];
164 if(isascii(kwinit
) && isupper(kwinit
))
165 kwinit
= tolower((unsigned char) kwinit
);
179 user_flag_is_set(MAILSTREAM
*stream
, long unsigned int rawno
, char *keyword
)
184 if(stream
&& keyword
&& keyword
[0]){
185 if(rawno
> 0L && stream
186 && rawno
<= stream
->nmsgs
187 && (mc
= mail_elt(stream
, rawno
)) != NULL
){
188 j
= user_flag_index(stream
, keyword
);
189 if(j
>= 0 && j
< NUSERFLAGS
&& ((1 << j
) & mc
->user_flags
))
199 * Returns the bit position of the keyword in stream, else -1.
202 user_flag_index(MAILSTREAM
*stream
, char *keyword
)
207 if(stream
&& keyword
&& keyword
[0]){
208 for(i
= 0; i
< NUSERFLAGS
; i
++)
209 if((p
=stream_to_user_flag_name(stream
, i
)) && !strucmp(keyword
, p
)){
220 stream_to_user_flag_name(MAILSTREAM
*stream
, int k
)
222 if(stream
&& stream
->user_flags
&& k
>= 0 && k
< NUSERFLAGS
)
223 return(stream
->user_flags
[k
]);
230 some_user_flags_defined(MAILSTREAM
*stream
)
235 for(k
= 0; k
< NUSERFLAGS
; k
++)
236 if(stream_to_user_flag_name(stream
, k
))
243 /*----------------------------------------------------------------------
244 Build flags string based on requested flags and what's set in messagecache
246 Args: flags -- flags to test
248 Result: allocated, space-delimited flags string is returned
251 flag_string(MAILSTREAM
*stream
, long rawno
, long int flags
)
254 char *p
, *q
, *returned_flags
= NULL
;
258 mc
= (rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
)
259 ? mail_elt(stream
, rawno
) : NULL
;
261 return returned_flags
;
263 if((flags
& F_DEL
) && mc
->deleted
)
264 len
+= strlen("\\DELETED") + 1;
266 if((flags
& F_ANS
) && mc
->answered
)
267 len
+= strlen("\\ANSWERED") + 1;
269 if((flags
& F_FWD
) && user_flag_is_set(stream
, rawno
, FORWARDED_FLAG
))
270 len
+= strlen(FORWARDED_FLAG
) + 1;
272 if((flags
& F_FLAG
) && mc
->flagged
)
273 len
+= strlen("\\FLAGGED") + 1;
275 if((flags
& F_SEEN
) && mc
->seen
)
276 len
+= strlen("\\SEEN") + 1;
278 if((flags
& F_KEYWORD
) && stream
->user_flags
){
279 for(k
= 0; k
< NUSERFLAGS
; k
++){
280 if((q
=stream_to_user_flag_name(stream
, k
))
281 && user_flag_is_set(stream
, rawno
, q
)){
282 len
+= strlen(q
) + 1;
287 returned_flags
= (char *) fs_get((len
+1) * sizeof(char));
291 if((flags
& F_DEL
) && mc
->deleted
)
292 sstrncpy(&p
, "\\DELETED ", len
+1-(p
-returned_flags
));
294 if((flags
& F_ANS
) && mc
->answered
)
295 sstrncpy(&p
, "\\ANSWERED ", len
+1-(p
-returned_flags
));
297 if((flags
& F_FWD
) && user_flag_is_set(stream
, rawno
, FORWARDED_FLAG
)){
298 sstrncpy(&p
, FORWARDED_FLAG
, len
+1-(p
-returned_flags
));
299 sstrncpy(&p
, " ", len
+1-(p
-returned_flags
));
302 if((flags
& F_FLAG
) && mc
->flagged
)
303 sstrncpy(&p
, "\\FLAGGED ", len
+1-(p
-returned_flags
));
305 if((flags
& F_SEEN
) && mc
->seen
)
306 sstrncpy(&p
, "\\SEEN ", len
+1-(p
-returned_flags
));
308 if((flags
& F_KEYWORD
) && stream
->user_flags
){
309 for(k
= 0; k
< NUSERFLAGS
; k
++){
310 if((q
=stream_to_user_flag_name(stream
, k
))
311 && user_flag_is_set(stream
, rawno
, q
)){
312 sstrncpy(&p
, q
, len
+1-(p
-returned_flags
));
313 sstrncpy(&p
, " ", len
+1-(p
-returned_flags
));
314 len
+= strlen(p
) + 1;
319 if(p
!= returned_flags
&& (len
+1-(p
-returned_flags
)>0))
322 return returned_flags
;
327 get_msgno_by_msg_id(MAILSTREAM
*stream
, char *message_id
, MSGNO_S
*msgmap
)
329 SEARCHPGM
*pgm
= NULL
;
330 long hint
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
334 extern MAILSTREAM
*mm_search_stream
;
335 extern long mm_search_count
;
337 if(!(message_id
&& message_id
[0]) || stream
->nmsgs
< 1L)
340 mm_search_count
= 0L;
341 mm_search_stream
= stream
;
342 while(mm_search_count
== 0L && iter
++ < 3
343 && (pgm
= mail_newsearchpgm()) != NULL
){
344 pgm
->message_id
= mail_newstringlist();
345 pgm
->message_id
->text
.data
= (unsigned char *) cpystr(message_id
);
346 pgm
->message_id
->text
.size
= strlen(message_id
);
348 if(iter
> 1 || hint
> stream
->nmsgs
)
352 /* restrict to hint message on first try */
353 pgm
->msgno
= mail_newsearchset();
354 pgm
->msgno
->first
= pgm
->msgno
->last
= hint
;
357 /* restrict to last 50 messages on 2nd try */
358 pgm
->msgno
= mail_newsearchset();
359 if(stream
->nmsgs
> 100L)
360 pgm
->msgno
->first
= stream
->nmsgs
-50L;
362 pgm
->msgno
->first
= 1L;
366 pgm
->msgno
->last
= stream
->nmsgs
;
369 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
372 for(newmsgno
=stream
->nmsgs
; newmsgno
> 0L; newmsgno
--)
373 if((mc
= mail_elt(stream
, newmsgno
)) && mc
->searched
)
378 return(mn_raw2m(msgmap
, newmsgno
));
383 * These chars are not allowed in keywords.
385 * Returns 0 if ok, 1 if not.
386 * Returns an allocated error message on error.
389 keyword_check(char *kw
, char **error
)
397 kw
= nick_to_keyword(kw
);
399 for(p
= kw
; p
&& *p
; p
++)
402 *error
= cpystr("Keywords must be all ASCII characters");
407 if((t
= strindex(kw
, SPACE
)) ||
408 (t
= strindex(kw
, '{')) ||
409 (t
= strindex(kw
, '(')) ||
410 (t
= strindex(kw
, ')')) ||
411 (t
= strindex(kw
, ']')) ||
412 (t
= strindex(kw
, '%')) ||
413 (t
= strindex(kw
, '"')) ||
414 (t
= strindex(kw
, '\\')) ||
415 (t
= strindex(kw
, '*'))){
422 snprintf(buf
, sizeof(buf
), "%s not allowed in keywords",
430 *error
= cpystr(buf
);