#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: keyword.c 786 2007-11-02 23:23:04Z hubert@u.washington.edu $";
#endif

/*
 * ========================================================================
 * Copyright 2006-2007 University of Washington
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * ========================================================================
 */

#include "../pith/headers.h"
#include "../pith/keyword.h"
#include "../pith/state.h"
#include "../pith/flag.h"
#include "../pith/string.h"
#include "../pith/status.h"
#include "../pith/util.h"


/*
 * Internal prototypes
 */


/*
 * Read the keywords array into a KEYWORD_S structure.
 * Make sure that all of the strings are UTF-8.
 */
KEYWORD_S *
init_keyword_list(char **keywordarray)
{
    char     **t, *nickname, *keyword;
    KEYWORD_S *head = NULL, **tail;

    tail = &head;
    for(t = keywordarray; t && *t && **t; t++){
	nickname = keyword = NULL;
	get_pair(*t, &nickname, &keyword, 0, 0);
	*tail = new_keyword_s(keyword, nickname);
	tail  = &(*tail)->next;

	if(keyword)
	  fs_give((void **) &keyword);

	if(nickname)
	  fs_give((void **) &nickname);
    }

    return(head);
}


KEYWORD_S *
new_keyword_s(char *keyword, char *nickname)
{
    KEYWORD_S *kw = NULL;

    kw = (KEYWORD_S *) fs_get(sizeof(*kw));
    memset(kw, 0, sizeof(*kw));

    if(keyword && *keyword)
      kw->kw = cpystr(keyword);

    if(nickname && *nickname)
      kw->nick = cpystr(nickname);
    
    return(kw);
}


void
free_keyword_list(KEYWORD_S **kl)
{
    if(kl && *kl){
	if((*kl)->next)
	  free_keyword_list(&(*kl)->next);

	if((*kl)->kw)
	  fs_give((void **) &(*kl)->kw);

	if((*kl)->nick)
	  fs_give((void **) &(*kl)->nick);

	fs_give((void **) kl);
    }
}


/*
 * Return a pointer to the keyword associated with a nickname, or the
 * input itself if no match.
 */
char *
nick_to_keyword(char *nick)
{
    KEYWORD_S *kw;
    char      *ret;

    ret = nick;
    for(kw = ps_global->keywords; kw; kw = kw->next)
      if(!strcmp(nick, kw->nick ? kw->nick : kw->kw ? kw->kw : "")){
	  if(kw->nick)
	    ret = kw->kw;

	  break;
      }
    
    return(ret);
}


/*
 * Return a pointer to the nickname associated with a keyword, or the
 * input itself if no match.
 */
char *
keyword_to_nick(char *keyword)
{
    KEYWORD_S *kw;
    char      *ret;

    ret = keyword;
    for(kw = ps_global->keywords; kw; kw = kw->next)
      if(!strcmp(keyword, kw->kw ? kw->kw : "")){
	  if(kw->nick)
	    ret = kw->nick;

	  break;
      }
    
    return(ret);
}


/*
 * Return a pointer to the keyword associated with an initial, or the
 * input itself if no match.
 * Only works for ascii initials.
 */
char *
initial_to_keyword(char *initial)
{
    KEYWORD_S *kw;
    char      *ret;
    int        init;
    int        kwinit;

    ret = initial;
    if(initial[0] && initial[1] == '\0'){
	init = initial[0];
	if(isascii(init) && isupper(init))
	  init = tolower((unsigned char) init);

	for(kw = ps_global->keywords; kw; kw = kw->next){
	    if(kw->nick)
	      kwinit = kw->nick[0];
	    else if(kw->kw)
	      kwinit = kw->kw[0];

	    if(isascii(kwinit) && isupper(kwinit))
	      kwinit = tolower((unsigned char) kwinit);

	    if(kwinit == init){
		ret = kw->kw;
		break;
	    }
	}
    }
    
    return(ret);
}


int
user_flag_is_set(MAILSTREAM *stream, long unsigned int rawno, char *keyword)
{
    int           j, is_set = 0;
    MESSAGECACHE *mc;

    if(stream && keyword && keyword[0]){
	if(rawno > 0L && stream
	   && rawno <= stream->nmsgs
	   && (mc = mail_elt(stream, rawno)) != NULL){
	    j = user_flag_index(stream, keyword);
	    if(j >= 0 && j < NUSERFLAGS && ((1 << j) & mc->user_flags))
	      is_set++;
	}
    }
	
    return(is_set);
}


/*
 * Returns the bit position of the keyword in stream, else -1.
 */
int
user_flag_index(MAILSTREAM *stream, char *keyword)
{
    int i, retval = -1;
    char *p;

    if(stream && keyword && keyword[0]){
	for(i = 0; i < NUSERFLAGS; i++)
	  if((p=stream_to_user_flag_name(stream, i)) && !strucmp(keyword, p)){
	      retval = i;
	      break;
	  }
    }

    return(retval);
}


char *
stream_to_user_flag_name(MAILSTREAM *stream, int k)
{
    if(stream && stream->user_flags && k >= 0 && k < NUSERFLAGS)
      return(stream->user_flags[k]);

    return(NULL);
}


int
some_user_flags_defined(MAILSTREAM *stream)
{
    int k;

    if(stream)
      for(k = 0; k < NUSERFLAGS; k++)
	if(stream_to_user_flag_name(stream, k))
	  return 1;

    return 0;
}


/*----------------------------------------------------------------------
  Build flags string based on requested flags and what's set in messagecache

   Args: flags -- flags to test

 Result: allocated, space-delimited flags string is returned
 ----*/
char *
flag_string(MAILSTREAM *stream, long rawno, long int flags)
{
    MESSAGECACHE *mc;
    char *p, *q, *returned_flags = NULL;
    size_t len = 0;
    int k;

    mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
			    ? mail_elt(stream, rawno) : NULL;
    if(!mc)
      return returned_flags;

    if((flags & F_DEL) && mc->deleted)
      len += strlen("\\DELETED") + 1;

    if((flags & F_ANS) && mc->answered)
      len += strlen("\\ANSWERED") + 1;

    if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG))
      len += strlen(FORWARDED_FLAG) + 1;

    if((flags & F_FLAG) && mc->flagged)
      len += strlen("\\FLAGGED") + 1;

    if((flags & F_SEEN) && mc->seen)
      len += strlen("\\SEEN") + 1;

    if((flags & F_KEYWORD) && stream->user_flags){
	for(k = 0; k < NUSERFLAGS; k++){
	    if((q=stream_to_user_flag_name(stream, k))
	       && user_flag_is_set(stream, rawno, q)){
		len += strlen(q) + 1;
	    }
	}
    }

    returned_flags = (char *) fs_get((len+1) * sizeof(char));
    p = returned_flags;
    *p = '\0';

    if((flags & F_DEL) && mc->deleted)
      sstrncpy(&p, "\\DELETED ", len+1-(p-returned_flags));

    if((flags & F_ANS) && mc->answered)
      sstrncpy(&p, "\\ANSWERED ", len+1-(p-returned_flags));

    if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
	sstrncpy(&p, FORWARDED_FLAG, len+1-(p-returned_flags));
	sstrncpy(&p, " ", len+1-(p-returned_flags));
    }

    if((flags & F_FLAG) && mc->flagged)
      sstrncpy(&p, "\\FLAGGED ", len+1-(p-returned_flags));

    if((flags & F_SEEN) && mc->seen)
      sstrncpy(&p, "\\SEEN ", len+1-(p-returned_flags));

    if((flags & F_KEYWORD) && stream->user_flags){
	for(k = 0; k < NUSERFLAGS; k++){
	    if((q=stream_to_user_flag_name(stream, k))
	       && user_flag_is_set(stream, rawno, q)){
		sstrncpy(&p, q, len+1-(p-returned_flags));
		sstrncpy(&p, " ", len+1-(p-returned_flags));
		len += strlen(p) + 1;
	    }
	}
    }

    if(p != returned_flags && (len+1-(p-returned_flags)>0))
      *--p = '\0';

    return returned_flags;
}


long
get_msgno_by_msg_id(MAILSTREAM *stream, char *message_id, MSGNO_S *msgmap)
{
    SEARCHPGM  *pgm = NULL;
    long        hint = mn_m2raw(msgmap, mn_get_cur(msgmap));
    long        newmsgno = -1L;
    int         iter = 0;
    MESSAGECACHE *mc;
    extern MAILSTREAM *mm_search_stream;
    extern long        mm_search_count;

    if(!(message_id && message_id[0]) || stream->nmsgs < 1L)
      return(newmsgno);

    mm_search_count = 0L;
    mm_search_stream = stream;
    while(mm_search_count == 0L && iter++ < 3
	  && (pgm = mail_newsearchpgm()) != NULL){
	pgm->message_id = mail_newstringlist();
	pgm->message_id->text.data = (unsigned char *) cpystr(message_id);
	pgm->message_id->text.size = strlen(message_id);

	if(iter > 1 || hint > stream->nmsgs)
	  iter++;

	if(iter == 1){
	    /* restrict to hint message on first try */
	    pgm->msgno = mail_newsearchset();
	    pgm->msgno->first = pgm->msgno->last = hint;
	}
	else if(iter == 2){
	    /* restrict to last 50 messages on 2nd try */
	    pgm->msgno = mail_newsearchset();
	    if(stream->nmsgs > 100L)
	      pgm->msgno->first = stream->nmsgs-50L;
	    else{
		pgm->msgno->first = 1L;
		iter++;
	    }

	    pgm->msgno->last = stream->nmsgs;
	}

	pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);

	if(mm_search_count){
	    for(newmsgno=stream->nmsgs; newmsgno > 0L; newmsgno--)
	      if((mc = mail_elt(stream, newmsgno)) && mc->searched)
		break;
	}
    }

    return(mn_raw2m(msgmap, newmsgno));
}


/*
 * These chars are not allowed in keywords.
 *
 * Returns 0 if ok, 1 if not.
 * Returns an allocated error message on error.
 */
int
keyword_check(char *kw, char **error)
{
    register char *t;
    char buf[100], *p;

    if(!kw || !kw[0])
      return 1;

    kw = nick_to_keyword(kw);

    for(p = kw; p && *p; p++)
      if(!isascii(*p)){
	if(error)
	  *error = cpystr("Keywords must be all ASCII characters");

	return 1;
      }

    if((t = strindex(kw, SPACE)) ||
       (t = strindex(kw, '{'))   ||
       (t = strindex(kw, '('))   ||
       (t = strindex(kw, ')'))   ||
       (t = strindex(kw, ']'))   ||
       (t = strindex(kw, '%'))   ||
       (t = strindex(kw, '"'))   ||
       (t = strindex(kw, '\\'))  ||
       (t = strindex(kw, '*'))){
	char s[4];
	s[0] = '"';
	s[1] = *t;
	s[2] = '"';
	s[3] = '\0';
	if(error){
	    snprintf(buf, sizeof(buf), "%s not allowed in keywords",
		*t == SPACE ?
		    "Spaces" :
		    *t == '"' ?
			"Quotes" :
			*t == '%' ?
			    "Percents" :
			    s);
	    *error = cpystr(buf);
	}

	return 1;
    }

    return 0;
}
