#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>		/* Directory information.       */              


#include "global.h"

typedef struct symlink_node
{
 char mail_dir[MAX_PATH + 1];
 char link_fname[MAX_PATH + 1];
 int resolved;
 struct symlink_node *next;
} SYMLINK_NODE;

SYMLINK_NODE *head = 0;

int symlinks_found = 0;
int symlinks_processed = 0;




static void
add_to_unresolved_list(const char *mail_dir, const char *link_fname)
{
SYMLINK_NODE *ptr;

 if(!head)
  { 
   head = (SYMLINK_NODE *) malloc(sizeof(SYMLINK_NODE));
   ptr = head;
  }
 else
  {
   for(ptr=head; ptr->next; ptr=ptr->next);
   ptr->next  =   (SYMLINK_NODE *) malloc(sizeof(SYMLINK_NODE));
   ptr = ptr->next;
  }
  
  
  strncpy(ptr->mail_dir, mail_dir, MAX_PATH + 1);
  strncpy(ptr->link_fname, link_fname, MAX_PATH + 1);  
  ptr->resolved = 0;
  ptr->next = (SYMLINK_NODE *) 0;
}  
  
  



int path_is_symlink(const char *path)
{

 struct stat buf;
 
   if(lstat (path, &buf))
      return 0;
      
  return  S_ISLNK (buf.st_mode);

}



int
is_symlink (const char *dirname, const char *fname)
{
  char path[MAX_PATH + 1];

  snprintf (path, MAX_PATH + 1, "%s/%s", dirname, fname);

  return path_is_symlink(path);
}






int
is_directory (const char *dirname, const char *fname)
{
  struct stat buf;
  char path[MAX_PATH + 1];

  snprintf (path, MAX_PATH + 1, "%s/%s", dirname, fname);

  stat (path, &buf);
  return S_ISDIR (buf.st_mode);
}



static void
make_hard_link(const char *fname, const char *link_end)
{
 unlink(fname);
 if(link(link_end, fname))
    perror(link_end);
 else
    symlinks_processed++;   
}

 




static
void process_symlink(const char *mail_path, const char *fname)
{
 char link_path[MAX_PATH + 1];
 char link_end[MAX_PATH + 1];
 char current_dir[MAX_PATH + 1];
 int len;

  snprintf(link_path, MAX_PATH + 1, "%s/%s", mail_path, fname);
  
  len = readlink(link_path, link_end, MAX_PATH);
  if(len == -1)
    {
      perror(link_path);
    }
  else
  {
    *(link_end + len) = '\0';
    
     /* get current directory so we can return */
    if(!getcwd(current_dir, MAX_PATH + 1))
       perror("Can't get current working directory.");

    symlinks_found++;
    
    if(chdir(mail_path))
        perror(mail_path);

    if(path_is_symlink(link_end))
      {
         add_to_unresolved_list(mail_path,fname);
      }
     else
      {
         make_hard_link(fname, link_end);
      }   
    
    if(chdir(current_dir))
        perror(current_dir);
  }
}    
    





static 
void do_person(const char *people_path, const char *person)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char mail_path[MAX_PATH + 1];
  
  if(is_directory(people_path, person))
   {
//     printf("Checking user: %s...\n", person);
     printf(".");
     fflush(stdout);
     snprintf(mail_path, MAX_PATH + 1, "%s/%s/mail", people_path, person);
     
     dir_p = opendir (mail_path);
     if (!dir_p)
      {
       perror("Unable to open 'mail' directory, so skipping");
       return;
     }

    while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, ".."))
	  {
	    if(strstr(dir_entry_p->d_name, ".fil") && is_symlink(mail_path, dir_entry_p->d_name))
	          process_symlink(mail_path, dir_entry_p->d_name);
	  }       
    }

    closedir (dir_p);
  } 
}









static 
void do_course(const char *group_path, const char *course)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char people_path[MAX_PATH + 1];
  
  if(is_directory(group_path, course))
   {
//     printf("Entering course: %s...\n", course);
     snprintf(people_path, MAX_PATH + 1, "%s/%s/people", group_path, course);
     
     dir_p = opendir (people_path);
     if (!dir_p)
      {
       perror("Unable to open 'people' directory, so skipping");
       return;
     }

    while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, ".."))
  	       do_person(people_path, dir_entry_p->d_name);
    }

    closedir (dir_p);
  } 
}






void
do_course_group (const char *group)
{
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char group_path[MAX_PATH + 1];
  
  if(is_directory(COURSE_PATH, group))
   {
     snprintf(group_path, MAX_PATH + 1, "%s%s", COURSE_PATH, group);
//     printf("Entering course group: %s...\n", group_path);
     
     dir_p = opendir (group_path);
     if (!dir_p)
      {
       perror("Unable to open directory");
       return;
     }

    while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, ".."))
  	       do_course(group_path, dir_entry_p->d_name);
    }

    closedir (dir_p);
  } 
}



static
int symlink_resolved (SYMLINK_NODE *data)
{
 int len;
 char link_path[MAX_PATH + 1];
 char link_end[MAX_PATH + 1];
 char current_dir[MAX_PATH + 1];
 
  puts("in symlink_resolved()");
  
  snprintf(link_path, MAX_PATH + 1, "%s/%s", data->mail_dir, data->link_fname);

  len = readlink(link_path, link_end, MAX_PATH);
  if(len == -1)
    {
      perror(link_path);
      return 0;
    }
  else
  {
    *(link_end + len) = '\0';
    
     /* get current directory so we can return */
    if(!getcwd(current_dir, MAX_PATH + 1))
       perror("Can't get current working directory.");

    if(chdir(data->mail_dir))
        perror(data->mail_dir);

    if(path_is_symlink(link_end))
      {
        if(chdir(current_dir))
           perror(current_dir);
        return 0;
      }
    else
      {
        make_hard_link(data->link_fname, link_end);
        if(chdir(current_dir))
           perror(current_dir);
        return 1;
      }
   }
}   





static void
process_unresolved_list()
{
 SYMLINK_NODE *ptr;
 int success = 0; 

 
 do
  {
   success = 0;
   for(ptr=head; ptr; ptr=ptr->next)
    if(!ptr->resolved)
     {
        if(symlink_resolved(ptr))
            success = 1;
     }
   } while (success);
        
}




void
do_upgrade ()
{
  DIR *dir_p;
  struct dirent *dir_entry_p;

  dir_p = opendir (COURSE_PATH);
  if (!dir_p)
    {
     printf("\n\nUnable to open directory: %s\n", COURSE_PATH);
     printf("Be sure to run this from your installation's 'src' directory!\n\n");
     exit(1);
    }

  while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (strcmp (dir_entry_p->d_name, ".")
	  && strcmp (dir_entry_p->d_name, ".."))
  	       do_course_group(dir_entry_p->d_name);
    }

  closedir (dir_p);
  process_unresolved_list();
}






int main()
{

 do_upgrade(); 
 
 printf("\n\nFound %d symbolic links. Converted %d of them to hard links.\n\nDone!\n", symlinks_found, symlinks_processed);
 return 0;
}

 
 
 
