Milán Major

netscape-revival

1 branch
Code

lib/libmsg/msginit.c

/* -*- Mode: C; tab-width: 4 -*-
   init.c --- initialize mail/news contexts.
   Copyright © 1995 Netscape Communications Corporation, all rights reserved.
   Created: Terry Weissman <terry@netscape.com>, 19-Jun-95.
 */

#include "msg.h"
#include "xp_file.h"
#include "msgundo.h"
#include "mailsum.h"
#include "newsrc.h"


/* for XP_GetString() */
#include "xpgetstr.h"
extern int MK_MSG_CANT_CREATE_MAIL_DIR;
extern int MK_OUT_OF_MEMORY;
extern int MK_MSG_DIR_DOESNT_EXIST;


static void FreeFolders(MWContext* context, MSG_Folder* folder);

static int msg_folder_compare(const void* p1, const void* p2) 
{
  char *s1 = (*((MSG_Folder**) p1))->name;
  char *s2 = (*((MSG_Folder**) p2))->name;

#define MAGIC_FLAGS	(MSG_FOLDER_FLAG_INBOX | \
					 MSG_FOLDER_FLAG_TRASH | \
					 MSG_FOLDER_FLAG_QUEUE | \
					 MSG_FOLDER_FLAG_DRAFTS| \
					 MSG_FOLDER_FLAG_SENTMAIL)
  int magic1 = ((*((MSG_Folder**) p1))->flags) & MAGIC_FLAGS;
  int magic2 = ((*((MSG_Folder**) p2))->flags) & MAGIC_FLAGS;
#undef MAGIC_FLAGS

  /* First and foremost -- magic folders go first. */
  if (magic1 != magic2) {
	return magic2 - magic1;
  }

  /* Special hack - if one of them is a relative path and the other is not,
	 the non-relative path goes first, despite the fact that '/' < 'A'. */
  if (*s1 == '/' && *s2 != '/')
	return 1;
  else if (*s1 != '/' && *s2 == '/')
	return -1;

  return XP_FILENAMECMP (s1, s2);
}


/* Sorts the folders by name - does not recurse. */
int
msg_SortFolders(MSG_Folder **folder_list)
{
  int i, count;
  MSG_Folder **list;
  MSG_Folder *folder;
  if (!folder_list || !*folder_list)
	return 0;

  count = 0;
  for (folder = *folder_list; folder; folder = folder->next)
	count++;
  if (count < 2) return 0;

  list = (MSG_Folder**) XP_ALLOC(sizeof(MSG_Folder*) * count);
  if (!list) return MK_OUT_OF_MEMORY;

  for (folder = *folder_list,  i= 0; i < count ; folder = folder->next, i++)
	list[i] = folder;

  qsort (list, count, sizeof(MSG_Folder*), msg_folder_compare);

  for (i=count-2 ; i>=0 ; i--)
	list[i]->next = list[i+1];

  list[count-1]->next = NULL;

  *folder_list = list[0];
  XP_FREE(list);	
  return 0;
}


/*
 * Get the mail folders for the given directory.  This is a recursive
 * routine, but it only recurs the depth of the mail heirarchy, which had
 * better not be all that deep. In fact, we won't recur more than about
 * MAX_FOLDER_DEPTH times, so we won't get too screwed by self-referencing
 * symbolic links and nasty things like that.
 */

#define MAX_FOLDER_DEPTH		100

static MSG_Folder *
msg_walk_folder_tree (MWContext *context, const char* directory,
					  int curdepth, XP_Bool* isdir)
{
  XP_Dir dir;
  XP_DirEntryStruct* entry;
  MSG_Folder* first = NULL;
  MSG_Folder* prev = NULL;
  MSG_Folder* cur = NULL;
  XP_Bool dirp;

  if (isdir) *isdir = FALSE;
  if (curdepth >= MAX_FOLDER_DEPTH) return 0;	/* ### Put up dialog? */

#ifdef XP_UNIX
  {
  /* BSDI is a steaming pile.  On every other platform, if you call opendir
	 with a file, it returns NULL.  But that doesn't happen on BSDI, so we'll
	 just stat the sucker first.  And, oh, what the heck, maybe some other
	 Unix platform loses in the same way, so we'll do this on all of them. */

	XP_StatStruct folderst;
	if (XP_Stat(directory, &folderst, xpMailFolder)) {
	  return NULL;
	}

	if (!S_ISDIR(folderst.st_mode)) {
	  return NULL;
	}
  }
#endif /* XP_UNIX */

  dir = XP_OpenDir(directory, xpMailFolder);
  if (!dir) return NULL;

  if (isdir) *isdir = TRUE;

  for (entry = XP_ReadDir(dir) ; entry ; entry = XP_ReadDir(dir)) {
	if (entry->d_name[0] == '.' ||
		entry->d_name[0] == '#' ||
		entry->d_name[XP_STRLEN (entry->d_name) - 1] == '~')
	  continue;

#if defined (XP_WIN) || defined (XP_MAC)
	/* don't add summary files to the list of folders;
	   don't add popstate files to the list either, or rules (sort.dat). */
	if ((XP_STRLEN(entry->d_name) > 4 &&
		 !strcasecomp(entry->d_name + XP_STRLEN(entry->d_name) - 4, ".snm")) ||
		!strcasecomp(entry->d_name, "popstate.dat") ||
		!strcasecomp(entry->d_name, "sort.dat"))
	  continue;
#ifdef XP_MAC
	/* Ignore Eudora's TOC files */
	if ((XP_STRLEN(entry->d_name) > 4 &&
		!strcasecomp(entry->d_name + XP_STRLEN(entry->d_name) - 4, ".toc")))
		continue;
#endif
#endif

	cur = msg_MakeMailFolder (context, directory, entry->d_name);
	if (!cur) break;			/* Out of memory; treat as no more folders. */

	cur->children = msg_walk_folder_tree (context, cur->data.mail.pathname,
										  curdepth + 1, &dirp);
	if (dirp)
	  {
		cur->flags |= MSG_FOLDER_FLAG_DIRECTORY;
		/* don't lose if they create a directory called "inbox" */
		cur->flags &= (~(MSG_FOLDER_FLAG_INBOX |
						 MSG_FOLDER_FLAG_TRASH |
/*						 MSG_FOLDER_FLAG_DRAFTS | */
						 MSG_FOLDER_FLAG_QUEUE));
	  }
	else
	  {
		cur->flags &= (~MSG_FOLDER_FLAG_DIRECTORY);
	  }

	if (cur->children)
	  cur->flags |= MSG_FOLDER_FLAG_ELIDED;
	else
	  cur->flags &= (~MSG_FOLDER_FLAG_ELIDED);

	cur->next = NULL;
	if (prev) prev->next = cur;
	else {
	  XP_ASSERT(first == NULL);
	  first = cur;
	}
	prev = cur;
  }
  XP_CloseDir(dir);
  
  if (first == NULL) return NULL;

  msg_SortFolders(&first);

  return first;
}


#if 0
static void 
DumpFolders(MSG_Folder* folder, int depth) {
	int i;
	if (!folder) return;
	for ( ; folder ; folder = folder->next) {
		for (i=0 ; i<depth ; i++) {
			fprintf(stderr, "  ");
		}
		fprintf(stderr, "%s -- %s\n", folder->name,
				folder->data.mail.pathname);
		DumpFolders(folder->children, depth + 1);
	}
}
#endif


static XP_Bool
msg_check_for_inbox (MWContext* context, const char* dir, MSG_Frame* frame)	
{
  XP_Bool bReturn = TRUE;

if (frame->folders == NULL) {
	XP_Dir direntry = XP_OpenDir(dir, xpMailFolder);
	if (direntry)
	  XP_CloseDir (direntry);
	else {
	  char* buf;
	  buf = PR_smprintf(XP_GetString(MK_MSG_DIR_DOESNT_EXIST),
						dir);	/* ### Fix i18n ### */
	  if ((bReturn = FE_Confirm(context, buf))) {
		if (XP_MakeDirectory(dir, xpMailFolder) < 0) {
		  FE_Alert(context, XP_GetString(MK_MSG_CANT_CREATE_MAIL_DIR));
		}
	  }
	  XP_FREE(buf);
	}
  }
  return bReturn;
}


void
MSG_InitializeMailContext(MWContext* context)
{
  MSG_Frame* frame;
  const char* dir;
  /* Make sure the FE has called XP_AddContextToList(). */
  XP_ASSERT (context == XP_FindContextOfType (0, MWContextMail));

  msg_InterruptContext(context, FALSE);

  dir = FE_GetFolderDirectory(context);

  context->msgframe = (MSG_Frame*) XP_CALLOC(sizeof(MSG_Frame), 1);
  frame = context->msgframe;
  if (!frame) return;
  if (msg_undo_Initialize(context) < 0) {
	XP_FREE(context->msgframe);
	context->msgframe = NULL;
	return;
  }
  context->type = MWContextMail;
  frame->sort_key = MSG_SortByMessageNumber;
  frame->sort_forward_p = TRUE;
  frame->thread_p = TRUE;
  frame->all_messages_visible_p = TRUE;
  frame->inline_attachments_p = TRUE;

  frame->folders = msg_walk_folder_tree (context, dir, 0, NULL);

  msg_UpdateToolbar(context);
  msg_RedisplayFolders(context);
  msg_RebuildFolderMenus(context);

  if (msg_check_for_inbox (context, dir, frame)) {
	msg_EnsureInboxExists (context);
  }
}


void
MSG_MailDirectoryChanged (MWContext *context)
{

  const char* dir;

  context = XP_FindContextOfType (context, MWContextMail);

  if (!context || !context->msgframe ||  context->type != MWContextMail) 
	return;

  msg_InterruptContext(context, FALSE);

  dir = FE_GetFolderDirectory(context);

  /* Free up the previous folders */
  if (context->msgframe->msgs)
	msg_FreeFolderData(context->msgframe->msgs);
  FreeFolders(context, context->msgframe->folders);
  context->msgframe->folders = NULL;
  msg_undo_Initialize(context);
  context->msgframe->msgs = NULL;
  context->msgframe->num_selected_folders = 0;
  context->msgframe->num_selected_msgs = 0;

  /* Rebuild the new folders */
  context->msgframe->folders = msg_walk_folder_tree (context, dir, 0, NULL);

  msg_UpdateToolbar(context);
  msg_RedisplayFolders(context);
  msg_RebuildFolderMenus(context);

  if (msg_check_for_inbox (context, dir, context->msgframe)) {
	msg_EnsureInboxExists (context);
  }
}



void
MSG_InitializeNewsContext(MWContext* context)
{
  MSG_Frame* frame;

  /* Make sure the FE has called XP_AddContextToList(). */
  XP_ASSERT (context == XP_FindContextOfType (0, MWContextNews));

  msg_InterruptContext(context, FALSE);

  context->msgframe = (MSG_Frame*) XP_CALLOC(sizeof(MSG_Frame), 1);
  frame = context->msgframe;
  if (!frame) return;
  if (msg_undo_Initialize(context) < 0) {
	XP_FREE(context->msgframe);
	context->msgframe = NULL;
	return;
  }
  context->type = MWContextNews;
  frame->sort_key = MSG_SortByDate;
  frame->sort_forward_p = TRUE;
  frame->thread_p = TRUE;
  frame->inline_attachments_p = TRUE;
  frame->data.news.group_display_type = MSG_ShowSubscribedWithArticles;
  msg_UpdateToolbar(context);
  msg_ShowNewsHosts (context);

  /* If there is exactly one news host, arrange to select it by default. */
  if (frame->folders && !frame->folders->next)
	{
	  XP_ASSERT ((frame->folders->flags & MSG_FOLDER_FLAG_ELIDED) &&
				 (frame->folders->flags & MSG_FOLDER_FLAG_NEWS_HOST));
	  if (frame->folders->flags & MSG_FOLDER_FLAG_ELIDED)
		MSG_ToggleFolderExpansion (context, 0);
	}
}


void
MSG_NewsDirectoryChanged ( MWContext *context )
{
  MSG_NewsHost* host;
  MSG_NewsHost* next;
 
  context = XP_FindContextOfType ( context, MWContextNews );

  if ( !context || !context->msgframe || context->type != MWContextNews )
	return;

  msg_InterruptContext ( context, FALSE );
	
	/* Free up the previous folders */
  if (context->msgframe->msgs)
	msg_FreeFolderData(context->msgframe->msgs);
  FreeFolders(context, context->msgframe->folders);
  context->msgframe->folders = NULL;
  msg_undo_Initialize(context);
  context->msgframe->msgs = NULL;
  context->msgframe->num_selected_folders = 0;
  context->msgframe->num_selected_msgs = 0;

  msg_FreeNewsrcSet(context->msgframe->data.news.knownarts.set);
  context->msgframe->data.news.knownarts.set = NULL;
  msg_SaveNewsRCFile (context);
  msg_FreeNewsRCFile (context->msgframe->data.news.newsrc_file);
  context->msgframe->data.news.newsrc_file = NULL;
  host = context->msgframe->data.news.hosts;
  while (host) {
	next = host->next;
	FREEIF(host->name);
	FREEIF(host->newsrc_file_name);
	XP_FREE(host);
	host = next;
  }
  context->msgframe->data.news.hosts = NULL;
  context->msgframe->data.news.current_host = NULL;

  msg_UpdateToolbar(context);
  msg_ShowNewsHosts (context);

  /* If there is exactly one news host, arrange to select it by default. */
  if (context->msgframe->folders && !context->msgframe->folders->next)
	{
	  XP_ASSERT ((context->msgframe->folders->flags & MSG_FOLDER_FLAG_ELIDED) &&
				 (context->msgframe->folders->flags & MSG_FOLDER_FLAG_NEWS_HOST));
	  if (context->msgframe->folders->flags & MSG_FOLDER_FLAG_ELIDED)
		MSG_ToggleFolderExpansion (context, 0);
	}
}

void
msg_FreeOneFolder(MWContext* context, MSG_Folder* folder)
{
  if (folder->flags & MSG_FOLDER_FLAG_NEWSGROUP) {
	FREEIF(folder->data.newsgroup.group_name);
  } else if (folder->flags & MSG_FOLDER_FLAG_MAIL) {
	FREEIF(folder->data.mail.pathname);
  }
  FREEIF(folder->name);
  XP_FREE(folder);
}

static void 
FreeFolders(MWContext* context, MSG_Folder* folder)
{
  MSG_Folder* next;
  for ( ; folder ; folder = next) {
	next = folder->next;
	FreeFolders(context, folder->children);
	msg_FreeOneFolder(context, folder);
  }
}


static void
msg_cleanup_context(MWContext* context)
{
  msg_FreeFolderData(context->msgframe->msgs);
  FreeFolders(context, context->msgframe->folders);
  msg_undo_Cleanup(context);
  FREEIF (context->msgframe->mail_to_sender_url);
  FREEIF (context->msgframe->mail_to_all_url);
  FREEIF (context->msgframe->post_reply_url);
  FREEIF (context->msgframe->post_and_mail_url);
  FREEIF (context->msgframe->displayed_message_subject);
  FREEIF (context->msgframe->displayed_message_id);
  FREEIF (context->msgframe->searchstring);
  XP_FREE(context->msgframe);
  context->msgframe = NULL;
}  

void
MSG_CleanupMailContext(MWContext* context)
{
  msg_InterruptContext(context, TRUE);
  msg_SaveMailSummaryChangesNow(context);

  if (context->msgframe->data.mail.scan_timer) {
	FE_ClearTimeout(context->msgframe->data.mail.scan_timer);
	context->msgframe->data.mail.scan_timer = NULL;
  }

#if 0
  if (context->msgframe->data.mail.biff_timer) {
	FE_ClearTimeout(context->msgframe->data.mail.biff_timer);
	context->msgframe->data.mail.biff_timer = NULL;
  }
#endif

  msg_FreeSortInfo(context);

  msg_cleanup_context(context);
}



void
MSG_CleanupNewsContext(MWContext* context)
{
  MSG_NewsHost* host;
  MSG_NewsHost* next;
  msg_InterruptContext(context, TRUE);
  msg_FreeNewsrcSet(context->msgframe->data.news.knownarts.set);
  msg_SaveNewsRCFile (context);
  msg_FreeNewsRCFile (context->msgframe->data.news.newsrc_file);
  host = context->msgframe->data.news.hosts;
  while (host) {
	next = host->next;
	FREEIF(host->name);
	FREEIF(host->newsrc_file_name);
	XP_FREE(host);
	host = next;
  }
  context->msgframe->data.news.hosts = NULL;
  msg_cleanup_context(context);
  NET_CleanTempXOVERCache();
}