* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / keyword.c
blob3af9e13fdb9245fd5a065f67ea6611c4ba46ba54
1 /*
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"
25 * Internal prototypes
30 * Read the keywords array into a KEYWORD_S structure.
31 * Make sure that all of the strings are UTF-8.
33 KEYWORD_S *
34 init_keyword_list(char **keywordarray)
36 char **t, *nickname, *keyword;
37 KEYWORD_S *head = NULL, **tail;
39 tail = &head;
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;
46 if(keyword)
47 fs_give((void **) &keyword);
49 if(nickname)
50 fs_give((void **) &nickname);
53 return(head);
57 KEYWORD_S *
58 new_keyword_s(char *keyword, char *nickname)
60 KEYWORD_S *kw = NULL;
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);
71 return(kw);
75 void
76 free_keyword_list(KEYWORD_S **kl)
78 if(kl && *kl){
79 if((*kl)->next)
80 free_keyword_list(&(*kl)->next);
82 if((*kl)->kw)
83 fs_give((void **) &(*kl)->kw);
85 if((*kl)->nick)
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.
97 char *
98 nick_to_keyword(char *nick)
100 KEYWORD_S *kw;
101 char *ret;
103 ret = nick;
104 for(kw = ps_global->keywords; kw; kw = kw->next)
105 if(!strcmp(nick, kw->nick ? kw->nick : kw->kw ? kw->kw : "")){
106 if(kw->nick)
107 ret = kw->kw;
109 break;
112 return(ret);
117 * Return a pointer to the nickname associated with a keyword, or the
118 * input itself if no match.
120 char *
121 keyword_to_nick(char *keyword)
123 KEYWORD_S *kw;
124 char *ret;
126 ret = keyword;
127 for(kw = ps_global->keywords; kw; kw = kw->next)
128 if(!strcmp(keyword, kw->kw ? kw->kw : "")){
129 if(kw->nick)
130 ret = kw->nick;
132 break;
135 return(ret);
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.
144 char *
145 initial_to_keyword(char *initial)
147 KEYWORD_S *kw;
148 char *ret;
149 int init;
150 int kwinit = 0;
152 ret = initial;
153 if(initial[0] && initial[1] == '\0'){
154 init = initial[0];
155 if(isascii(init) && isupper(init))
156 init = tolower((unsigned char) init);
158 for(kw = ps_global->keywords; kw; kw = kw->next){
159 if(kw->nick)
160 kwinit = kw->nick[0];
161 else if(kw->kw)
162 kwinit = kw->kw[0];
164 if(isascii(kwinit) && isupper(kwinit))
165 kwinit = tolower((unsigned char) kwinit);
167 if(kwinit == init){
168 ret = kw->kw;
169 break;
174 return(ret);
179 user_flag_is_set(MAILSTREAM *stream, long unsigned int rawno, char *keyword)
181 int j, is_set = 0;
182 MESSAGECACHE *mc;
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))
190 is_set++;
194 return(is_set);
199 * Returns the bit position of the keyword in stream, else -1.
202 user_flag_index(MAILSTREAM *stream, char *keyword)
204 int i, retval = -1;
205 char *p;
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)){
210 retval = i;
211 break;
215 return(retval);
219 char *
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]);
225 return(NULL);
230 some_user_flags_defined(MAILSTREAM *stream)
232 int k;
234 if(stream)
235 for(k = 0; k < NUSERFLAGS; k++)
236 if(stream_to_user_flag_name(stream, k))
237 return 1;
239 return 0;
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
249 ----*/
250 char *
251 flag_string(MAILSTREAM *stream, long rawno, long int flags)
253 MESSAGECACHE *mc;
254 char *p, *q, *returned_flags = NULL;
255 size_t len = 0;
256 int k;
258 mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
259 ? mail_elt(stream, rawno) : NULL;
260 if(!mc)
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));
288 p = returned_flags;
289 *p = '\0';
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))
320 *--p = '\0';
322 return returned_flags;
326 long
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));
331 long newmsgno = -1L;
332 int iter = 0;
333 MESSAGECACHE *mc;
334 extern MAILSTREAM *mm_search_stream;
335 extern long mm_search_count;
337 if(!(message_id && message_id[0]) || stream->nmsgs < 1L)
338 return(newmsgno);
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)
349 iter++;
351 if(iter == 1){
352 /* restrict to hint message on first try */
353 pgm->msgno = mail_newsearchset();
354 pgm->msgno->first = pgm->msgno->last = hint;
356 else if(iter == 2){
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;
361 else{
362 pgm->msgno->first = 1L;
363 iter++;
366 pgm->msgno->last = stream->nmsgs;
369 pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
371 if(mm_search_count){
372 for(newmsgno=stream->nmsgs; newmsgno > 0L; newmsgno--)
373 if((mc = mail_elt(stream, newmsgno)) && mc->searched)
374 break;
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)
391 register char *t;
392 char buf[100], *p;
394 if(!kw || !kw[0])
395 return 1;
397 kw = nick_to_keyword(kw);
399 for(p = kw; p && *p; p++)
400 if(!isascii(*p)){
401 if(error)
402 *error = cpystr("Keywords must be all ASCII characters");
404 return 1;
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, '*'))){
416 char s[4];
417 s[0] = '"';
418 s[1] = *t;
419 s[2] = '"';
420 s[3] = '\0';
421 if(error){
422 snprintf(buf, sizeof(buf), "%s not allowed in keywords",
423 *t == SPACE ?
424 "Spaces" :
425 *t == '"' ?
426 "Quotes" :
427 *t == '%' ?
428 "Percents" :
430 *error = cpystr(buf);
433 return 1;
436 return 0;