#include "common.h"
#include "send.h"

/* Return TRUE if a forwarding loop exists, i.e., the String `system'
* is found more than 4 times in the return address.
*/
static int
forward_loop(char *addr, char *system)
{
       int len, found;

       found = 0;
       len = strlen(system);
       while(addr = strchr(addr, '!'))
               if (!strncmp(++addr, system, len)
                && addr[len] == '!' && ++found == 4)
                       return 1;
       return 0;
}


/* bind the destinations to the commands to be executed */
dest *
up_bind(dest *destp, message *mp, int checkforward)
{
       int i, li;
       dest *list[2], *bound, *dp;

       bound = nil;
       list[0] = destp;
       list[1] = nil;

       /*
        *  loop once to check for:
        *      - forwarding rights
        *      - addressing loops
        *      - illegal characters
        *      - characters that need escaping
        */
       for (dp = d_rm(&list[0]); dp != 0; dp = d_rm(&list[0])) {
               if(!checkforward)
                       dp->authorized = 1;
               dp->addr = escapespecial(dp->addr);
               if (forward_loop(s_to_c(dp->addr), thissys)) {
                       dp->status = d_eloop;
                       d_same_insert(&bound, dp);
               } else if(forward_loop(s_to_c(mp->sender), thissys)) {
                       dp->status = d_eloop;
                       d_same_insert(&bound, dp);
               } else if(shellchars(s_to_c(dp->addr))) {
                       dp->status = d_syntax;
                       d_same_insert(&bound, dp);
               } else
                       d_insert(&list[1], dp);
       }
       li = 1;

       /* Loop until all addresses are bound or address loop detected */
       for (i=0; list[li]!=0 && i<32; ++i, li ^= 1) {
               /* Traverse the current list.  Bound items are put on the
                * `bound' list.  Unbound items are put on the next list to
                * traverse, `list[li^1]'.
                */
               for (dp = d_rm(&list[li]); dp != 0; dp = d_rm(&list[li])){
                       dest *newlist;

                       rewrite(dp, mp);
                       if(debug)
                               fprint(2, "%s -> %s\n", s_to_c(dp->addr),
                                       dp->repl1 ? s_to_c(dp->repl1):"");
                       switch (dp->status) {
                       case d_auth:
                               /* authorize address if not already authorized */
                               if(!dp->authorized){
                                       authorize(dp);
                                       if(dp->status==d_auth)
                                               d_insert(&list[li^1], dp);
                                       else
                                               d_insert(&bound, dp);
                               }
                               break;
                       case d_cat:
                               /* address -> local */
                               newlist = expand_local(dp);
                               if (newlist == 0) {
                                       /* append to mailbox (or error) */
                                       d_same_insert(&bound, dp);
                               } else if (newlist->status == d_undefined) {
                                       /* Forward to ... */
                                       d_insert(&list[li^1], newlist);
                               } else {
                                       /* Pipe to ... */
                                       d_same_insert(&bound, newlist);
                               }
                               break;
                       case d_pipe:
                               /* address -> command */
                               d_same_insert(&bound, dp);
                               break;
                       case d_alias:
                               /* address -> rewritten address */
                               newlist = s_to_dest(dp->repl1, dp);
                               if(newlist != 0)
                                       d_insert(&list[li^1], newlist);
                               else
                                       d_same_insert(&bound, dp);
                               break;
                       case d_translate:
                               /* pipe to a translator */
                               newlist = translate(dp);
                               if (newlist != 0)
                                       d_insert(&list[li^1], newlist);
                               else
                                       d_same_insert(&bound, dp);
                               break;
                       default:
                               /* error */
                               d_same_insert(&bound, dp);
                               break;
                       }
               }
       }

       /* mark remaining comands as "forwarding loops" */
       for (dp = d_rm(&list[li]); dp != 0; dp = d_rm(&list[li])) {
               dp->status = d_loop;
               d_same_insert(&bound, dp);
       }

       return bound;
}