/**********************************************************************
 *
 * fnord.cpp
 * Copyright (C) 1996
 * 
 * A component of the fnord webserver written by bmorin@wpi.edu.
 *
 * Altered for use with the Greenstone digital library software by the
 * New Zealand Digital Library Project at the University of Waikato,
 * New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "text_t.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#pragma hdrstop
#include "netio.h"
#include "settings.h"
#include "httpsrv.h"
#include "resource.h"
#include "locate.h"
#include "cgiwrapper.h"
#include "startbrowser.h"
#include "d_winsock.h"
#include "fileutil.h"

#define MAINWINDOWWIDTH 400
#define MAINWINDOWHEIGHT 200

#define WASTEHEIGHT 44  // Error in ht due to caption and menu bar
#define WASTEWIDTH 6
#define SPACERWIDTH 5

// size and position of the collection title
#define COLTITLEWIDTH 351
#define COLTITLEHEIGHT 35
#define COLTITLEX ((MAINWINDOWWIDTH-COLTITLEWIDTH)/2)
#define COLTITLEY 10

// size and position of the version string
#define VERSIONWIDTH MAINWINDOWWIDTH
#define VERSIONHEIGHT 20
#define VERSIONX 0
#define VERSIONY (COLTITLEY+COLTITLEHEIGHT)

// size and position of the Greenstone digital library logo
#define LOGOWIDTH 135
#define LOGOHEIGHT 55
#define LOGOX (MAINWINDOWWIDTH-(LOGOWIDTH+SPACERWIDTH))
#define LOGOY (MAINWINDOWHEIGHT-(LOGOHEIGHT+SPACERWIDTH))

// size and position of the enter button
#define ENTERBUTTONWIDTH 160
#define ENTERBUTTONHEIGHT 30
#define ENTERBUTTONX SPACERWIDTH
#define ENTERBUTTONY ((MAINWINDOWHEIGHT-(LOGOHEIGHT+SPACERWIDTH))-(ENTERBUTTONHEIGHT+SPACERWIDTH))

// size and position of the enter string
#define ENTERSTRINGX (ENTERBUTTONX+ENTERBUTTONWIDTH+SPACERWIDTH)
#define ENTERSTRINGY ENTERBUTTONY
#define ENTERSTRINGWIDTH (MAINWINDOWWIDTH-(ENTERBUTTONWIDTH+SPACERWIDTH+SPACERWIDTH+SPACERWIDTH))
#define ENTERSTRINGHEIGHT ENTERBUTTONHEIGHT

// size and position of the info string
#define INFOWIDTH MAINWINDOWWIDTH
#define INFOHEIGHT (MAINWINDOWHEIGHT-INFOY)
#define INFOX 0
#define INFOY ENTERBUTTONY

// size and position of restricted buttons
#define ALTERNATIVEBUTTONWIDTH 160
#define ALTERNATIVEBUTTONHEIGHT 30
#define ALTERNATIVEBUTTONX SPACERWIDTH
#define ALTERNATIVEBUTTONY LOGOY

#define INFOBUTTONWIDTH 30

// size and position of restart button
#define RESTARTBUTTONWIDTH 160
#define RESTARTBUTTONHEIGHT 30
#define RESTARTBUTTONX 50
#define RESTARTBUTTONY (MAINWINDOWHEIGHT-RESTARTBUTTONHEIGHT-15)

// constant for restart/reconfigure windows message (same as in recpt/configaction.cpp)
// http://msdn.microsoft.com/en-us/library/ms644931%28v=VS.85%29.aspx on WM_USER
// (also RegisterWindowMessage Function at http://msdn.microsoft.com/en-us/library/ms644947%28v=VS.85%29.aspx
// http://msdn.microsoft.com/en-us/library/ms644927%28v=VS.85%29.aspx)
#define WM_RESTART WM_USER+1

const char versionstring[] = "version " GSDL_VERSION;

const char strnothing[] = "";
const char strinit[] = "Initialising...";
const char strsb[] = "Starting browser...";
const char strrestartlib[] = "Press the 'Restart Library' button to start a browser\n"
"and enter library";
const char strenterlib[] = "Press this button to begin using\n"
"the library";
const char *enterstring = NULL; // points to the current enter string
const char *infostring = NULL; // points to the current info string


// globals to do with networking and browsers
int have_networking = 0;
int netscapeneeded = 0;
char startbrowserdir[1024] = ""; // HARD LIMIT!!!!!


// stats to do with the last time the enter library button was pressed
// these stats are needed to make suggestions about the proxy
int enterlib_libaccessnum = -1; // -1 = NA
DWORD enterlib_time = 0;


enum { undefined_window, console_window, plain_window }
window_state;

HDC coltitledc = NULL;
HBITMAP defcoltitlebitmap = NULL, coltitlebitmap=NULL;

HDC logodc=NULL;
HBITMAP deflogobitmap=NULL, logobitmap=NULL;

HWND GoButton = NULL;
HWND Enter = NULL;
// HWND EnterRestricted = NULL;
// HWND InfoRestricted = NULL;
int init_type = 0;
int init_done = 0;


// Added an argument so you can specify not to do any of the windows orientated clean up - jmt12 18/11/2003
void finish_up(bool init_only) {
  // remember the current preferences
  write_settings("");

  if(init_only) {
    return;
  }
   
 // Shutdown the HTTP server
  EndHTTPServer();
  
  // Shutdown the main server modules
  CleanUpNetIO();
  //    RemoveFnordFromTray();
  
  // Unload the wsock32 dll
  d_UnloadWinsock();
  
  // Clean up graphics stuff from window
  
  if (defcoltitlebitmap != NULL)
    SelectObject (coltitledc, defcoltitlebitmap);
  if (coltitlebitmap != NULL)
    DeleteObject (coltitlebitmap);
  if (coltitledc != NULL)
    DeleteDC (coltitledc);
  
  if (deflogobitmap != NULL)
    SelectObject (logodc, deflogobitmap);
  if (logobitmap != NULL)
    DeleteObject (logobitmap);
  if (logodc != NULL)
    DeleteDC (logodc);
  
  // Shutdown the main window
  PostQuitMessage(0);
}


int overlap(int left, int top, int right, int bottom,
            RECT &r2)
{
  if (left <= r2.right && r2.left <= right &&
      top <= r2.bottom && r2.top <= bottom)
    return 1;
  else
    return 0;
}


void paint_window (HDC pdc, RECT &updateRect) {
  // make sure the various DCs are set up
  if (coltitledc == NULL) {
    coltitledc = CreateCompatibleDC(pdc);
    defcoltitlebitmap = (HBITMAP)SelectObject (coltitledc, coltitlebitmap);
  }
  
  if (logodc == NULL) {
    logodc = CreateCompatibleDC(pdc);
    deflogobitmap = (HBITMAP)SelectObject (logodc, logobitmap);
  }

  // remove any remaining sign of initial buttons
  if (init_done && (overlap (ENTERBUTTONX, ENTERBUTTONY, ENTERBUTTONX+ENTERBUTTONWIDTH,
		ALTERNATIVEBUTTONY+ALTERNATIVEBUTTONHEIGHT, updateRect))) {
    RECT initRect;
    initRect.left = ENTERBUTTONX;
    initRect.top = ENTERBUTTONY;
    initRect.right = ENTERBUTTONX+ENTERBUTTONWIDTH;
    initRect.bottom = ALTERNATIVEBUTTONY+ALTERNATIVEBUTTONHEIGHT;
    FillRect(pdc, &initRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
  }

  // update the collection title if needed
  if (overlap(COLTITLEX, COLTITLEY,
	      COLTITLEX+COLTITLEWIDTH, COLTITLEY+COLTITLEHEIGHT, 
	      updateRect)) {
    BitBlt (pdc, COLTITLEX, COLTITLEY, 
	    COLTITLEX+COLTITLEWIDTH, COLTITLEY+COLTITLEHEIGHT,
	    coltitledc, 0, 0, SRCCOPY);
  }
  
  // update the version string if needed
  if (overlap (VERSIONX, VERSIONY, 
	       VERSIONX+VERSIONWIDTH, VERSIONY+VERSIONHEIGHT,
	       updateRect)) {
    RECT versionRect;
    versionRect.left = VERSIONX;
    versionRect.top = VERSIONY;
    versionRect.right = VERSIONX+VERSIONWIDTH;
    versionRect.bottom = VERSIONY+VERSIONHEIGHT;
    DrawText(pdc, versionstring, -1, &versionRect, DT_CENTER);
  }
  
  // decide what we want to draw
  if (init_done && gsdl_show_console) {
    // we want to draw a "console" window
    if (window_state != console_window ||
	overlap (text_rect.left, text_rect.top,
		 text_rect.right, text_rect.bottom,
		 updateRect)) {
      refresh_console (pdc);
    }
    
    window_state = console_window;
    
  } else {
    // we want to draw a "plain" window
    
    // update the enter string if needed
    if (overlap (ENTERSTRINGX, ENTERSTRINGY, ENTERSTRINGX+ENTERSTRINGWIDTH, 
		 ENTERSTRINGY+ENTERSTRINGHEIGHT, updateRect)) {

	RECT enterstringRect;
	enterstringRect.left = ENTERSTRINGX;
	enterstringRect.top = ENTERSTRINGY;
	enterstringRect.right = ENTERSTRINGX+ENTERSTRINGWIDTH;
	enterstringRect.bottom = ENTERSTRINGY+ENTERSTRINGHEIGHT;
	FillRect(pdc, &enterstringRect, (HBRUSH)GetStockObject(WHITE_BRUSH));

	if ((enterstring != NULL) && (strlen(enterstring) > 0)) {
      
	  int cury = ENTERSTRINGY;
	  int startline = 0, here = 0;
	  while (enterstring[here] != '\0') {
	    if (enterstring[here] < ' ') {
	      if (here - startline > 0) {
		// output the text
		TextOut (pdc, ENTERSTRINGX, cury, 
			 &enterstring[startline], here-startline);
		cury += line_spacing;
	      }
	      startline = here+1;
	    }
	    ++here;
	  }
	  // output any remaining text
	  if (here - startline > 0) {
	    TextOut (pdc, ENTERSTRINGX, cury, &enterstring[startline], here-startline);
	  }
	}
    }
    
    window_state = plain_window;
  }
  
  // update the info string if needed
  if (init_done && !gsdl_show_console && 
      ((infostring != NULL) && overlap (INFOX, INFOY, 
					INFOX+INFOWIDTH, INFOY+INFOHEIGHT,
					updateRect))) {
    RECT infoRect;
    infoRect.left = INFOX;
    infoRect.top = INFOY;
    infoRect.right = INFOX+INFOWIDTH;
    infoRect.bottom = INFOY+INFOHEIGHT;
    FillRect(pdc, &infoRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
    DrawText(pdc, infostring, -1, &infoRect, DT_CENTER);
  }

  // update the logo (always)
  BitBlt (pdc, LOGOX, LOGOY, LOGOX+LOGOWIDTH, LOGOY+LOGOHEIGHT,
	  logodc, 0, 0, SRCCOPY);
}


void handle_painting (HWND Window) {
  HDC pdc;  PAINTSTRUCT ps;
  pdc = BeginPaint(Window, &ps);
  
  paint_window (pdc, ps.rcPaint);
  
  EndPaint(Window, &ps);
}


// returns 1 on success, 0 otherwise
int enter_library (HWND Window) {
  int res;
  
  if (infostring == strrestartlib) {

    // the "restart library" button was pressed so we want to reinitialize
    // greenstone, read in all the macrofiles again, etc.
    gsdl_init(false);
  }

  // get the url and attempt to start a browser
  if (have_networking) {
    // There are now four address resolution methods:
    //   0: Standard -- get local IP then resolve to a name
    //   1: IP only -- as above, but use IP only -- don't resolve to a name
    //   2: localhost -- always use localhost
    //   3: 127.0.0.1 -- always use 127.0.0.1
    char gsdl_address[100];
    char hostIP[25]; // 16 should be enough to represent an IP address + null byte

    DWORD local_ip = GetLocalIP();
    // always work out the hostIP:
    sprintf(hostIP, "%d.%d.%d.%d", local_ip & 0xFF, (local_ip >> 8) & 0xFF, (local_ip >> 16) & 0xFF, (local_ip >> 24) & 0xFF);

    if (local_ip == INADDR_ANY || gsdl_address_resolution_method == 3) {
      local_ip = 127 + (0 << 8) + (0 << 16) + (1 << 24);
    }

    if (gsdl_address_resolution_method == 1 || gsdl_address_resolution_method == 3) {
      sprintf(gsdl_address, "%d.%d.%d.%d", local_ip & 0xFF, (local_ip >> 8) & 0xFF, (local_ip >> 16) & 0xFF, (local_ip >> 24) & 0xFF);
    }
    else if (gsdl_address_resolution_method == 2) {
      sprintf(gsdl_address, "localhost");
    }
    else { 
      strcpy(gsdl_address, GetLocalName(NULL));
    }

    text_t url = "http://" + text_t(gsdl_address);

    gsdl_host_IP = text_t(hostIP);
    gsdl_url = url;
    write_settings(gsdl_url);

    if (gsdl_port_num != 80) {
      url += ":" + text_t(gsdl_port_num);
    }
    url += gsdl_enterlib;

    // add a unique ending so it will always result in a request
    text_t::const_iterator it = 
      findchar (gsdl_enterlib.begin(), gsdl_enterlib.end(), '?');
    if (it == gsdl_enterlib.end()) url.push_back ('?');
    else url.push_back ('&');
    int tcount = GetTickCount();
    url += "uq=" + text_t(tcount);
    
    // remember the library access number for later proxy checking
    enterlib_libaccessnum = libaccessnum;
    
    if (gsdl_start_browser) {

      // do a quick check to make sure we're using netscape when
      // we should be (it might have been just installed)
      if (netscapeneeded) gsdl_check_browser_settings (netscapeneeded);
    
      char *cstr_url = url.getcstr();
      if (strlen (startbrowserdir) <= 0) {
	res = startbrowser (cstr_url, gsdl_browser_exe, NULL);
	
      } else res = startbrowser (cstr_url, gsdl_browser_exe, startbrowserdir);
    
      delete []cstr_url;

    } else {
      return 1;
    }
  }
  
  if (res == SB_NOERROR) {
    // success !!
    
    // remember the time the browser was started for later proxy checking
    if (have_networking) enterlib_time = GetTickCount();
    
    return 1;
  }
  
  // no browser was started
  enterlib_libaccessnum = -1;
  
  if ((res == SB_FAIL_BADFORMAT) || (res == SB_FAIL_NOTFOUND)) {
    if (!netscapeneeded) { // any browser
      MessageBox(Window,
		 "Failed to start your chosen browser. It seems that your\n"
		 "chosen browser has been removed or corrupted.\n"
		 "Please check your browser choice by going to the\n"
		 "'Settings...' item under the 'File' menu.",
		 "Greenstone Digital Library Software", MB_OK);
    } else { // netscape used
      MessageBox(Window,
		 "Failed to start Netscape. It seems that Netscape has\n"
		 "been removed or corrupted. You need Netscape\n"
		 "installed to use the Greenstone Digital Library\n"
		 "software on non-networked machines.",
		 "Greenstone Digital Library Software", MB_OK);
    }
    
  } else {
    MessageBox(Window,
	       "Failed to start your browser. This may have been\n"
	       "because there was not enough memory available. Shut\n"
	       "down all other applications you have running, and\n"
	       "try again.",
	       "Greenstone Digital Library Software", MB_OK);
  }
  
  return 0; // failed
}


void install_netscape (HWND Window) {
  char installpath[256];
  
  // try to find the browser directory
  char *gsdlhome = gsdl_gsdlhome.getcstr();
  strcpy (installpath, gsdlhome); // use gsdlhome to find the CD-ROM drive...
  delete []gsdlhome;
  
  // remove any slashes from the pathname
  int len = strlen (installpath);
  while ((len > 0) && ((installpath[len-1] == '\\') || (installpath[len-1] == '/'))) {
    --len;
  }
  // remove '\gsdl'
  len = len - 5;
  installpath[len] = '\0';
  
  strcat (installpath, "\\netscape\\");
  
  // check this directory
  if (!cstrcheckdir(installpath)) {
    return;
  }
  
  // get the operating system information
  OSVERSIONINFO osver;
  memset(&osver, 0, sizeof(OSVERSIONINFO));
  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&osver);

  // get the install program for this operating system
  if (osver.dwPlatformId == VER_PLATFORM_WIN32s) {
    strcat (installpath, "n16e405.exe");
  } else {
    strcat (installpath, "n32e405.exe");
  }
  
  // run the install program
  int res = WinExec (installpath, SW_SHOW);
  if (res == 0) {
    // out of resources or memory
    MessageBox (Window,
		"Did not have enough resources or memory to run the\n"
		"install program. Try shutting down all other programs\n"
		"and trying again.",
		"Greenstone Digital Library Software",
		MB_OK|MB_APPLMODAL);
    
  } else if (res == ERROR_BAD_FORMAT) {
    // executable image is corrupt -- ????
    MessageBox (Window,
		"The install program seems to be corrupt.",
		"Greenstone Digital Library Software",
		MB_OK|MB_APPLMODAL);
    
  } else if ((res == ERROR_FILE_NOT_FOUND) || (res == ERROR_PATH_NOT_FOUND)) {
    // couldn't find the executable -- ????
    MessageBox (Window,
		"Could not find the install program. Try installing\n"
		"Netscape from the Greenstone Digital Library\n"
		"software's install program.",
		"Greenstone Digital Library Software",
		MB_OK|MB_APPLMODAL);
  }
}

void open_help () {
  char topdir[256], cmd[256];
  
  // try to find the browser directory
  char *gsdlhome = gsdl_gsdlhome.getcstr();
  strcpy (topdir, gsdlhome); // use gsdlhome to find the CD-ROM drive...
  delete []gsdlhome;
  strcpy (cmd, "notepad ");
  
  // remove any slashes from the pathname
  int len = strlen (topdir);
  while ((len > 0) && ((topdir[len-1] == '\\') || (topdir[len-1] == '/'))) {
    --len;
  }
  // remove the '\gsdl'
  len = len - 5;
  topdir[len] = '\0';

  strcat (topdir, "\\README.txt");
  strcat (cmd, topdir);
  
  WinExec (cmd, SW_SHOW);
}


LRESULT __stdcall WndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) {
  switch(Message) {
    // Constant Messages
  case WM_CREATE:
    break;
    
  case WM_COMMAND:
    if ((GoButton != NULL) && ((HWND)LParam == GoButton)) {
      // attempt to start a browser
      gsdl_start_browser = 1;
      if (enter_library (Window) && !gsdl_show_console) 
    	ShowWindow(Window,SW_MINIMIZE);

    } else if ((Enter != NULL) && ((HWND)LParam == Enter)) {
      init_done = 1;
      init_type = 1;

//      } else if ((EnterRestricted != NULL) && ((HWND)LParam == EnterRestricted)) {
//        init_done = 1;
//        init_type = 0;

//      } else if ((InfoRestricted != NULL) && ((HWND)LParam == InfoRestricted)) {
//        MessageBox (NULL, "The Greenstone system automatically determines whether your computer has\n"
//  		  "network software installed or is connected to a network.  It operates correctly\n"
//  		  "under any of these conditions.\n\n"
//  		  "The restricted version is intended for use when the standard one\n\n"
//  		  "   (a) causes an unwanted telephone dialup operation;\n"
//  		  "   (b) fails to run because network software is installed, but installed\n"
//  		  "         incorrectly.\n\n"
//  		  "The restricted version only works with Netscape (not Internet Explorer).\n\n"
//  		  "Unless these problems arise, you should always use the standard version\n"
//  		  "(press the 'Enter Library' button).\n",
//  		  "Greenstone Digital Library Software", 
//  		  MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);

    } else {
      switch (LOWORD(WParam)) {
      case ID_PROJECT_SETTINGS:
	Settings_Dialog(Window, netscapeneeded);
	break;
      case ID_INSTALL_NETSCAPE:
	install_netscape(Window);
	break;
      case IDHELP:
	open_help();
	break;
      case ID_PROJECT_EXIT:
        finish_up(false);
        break;
      default:
        break;
      }
    }
    break;
    
  case WM_PAINT:
    handle_painting(Window);
    break;
    
  case HTTP_SERVER_MSG:
    ProcessHTTPServerMsg(WParam, LParam);
    break;
    
  case WM_DESTROY:
    finish_up(false);
    break;
    
  default:
    //Unhandled Messages end up here (DefWindowProc)
    return DefWindowProc(Window, Message, WParam, LParam);
  }
  
  return(0);
}

// returns 0 if successful,
// otherwise a WSA error number
int tryinitnetwork (HANDLE Instance, HWND MsgWindow, char *path) {
  // try to load winsock
  int err = d_LoadWinsock (path);
  
  if (err == D_NOERROR) {
    // next, try to init netio
    err = InitNetIO();
    
    if (err == 0) {
      // next, try to start the http server
      err = StartHTTPServer(MsgWindow);
      
      if (err == 0) {
	// finally, get the host name (no error value
	// is returned from this function)
	GetLocalName((HINSTANCE)Instance);
	
      } else {
	// couldn't start the http server, 
	// deinit netio and unload winsock
	CleanUpNetIO();
	d_UnloadWinsock();
      }
      
    } else {
      // couldn't init netio, unload winsock
      d_UnloadWinsock();
    }
    
  } else {
    // couldn't load winsock
    err = WSASYSNOTREADY;
  }
  
  return err;
}


// inits all the network functionality
// returns 0 if an unrecoverable error occurred,
// and 1 if everything successfully initialised.
int initnetwork (HANDLE Instance, HWND MsgWindow) {
  OSVERSIONINFO osver;
  
  // get the operating system information
  memset(&osver, 0, sizeof(OSVERSIONINFO));
  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&osver);
  
  // first try to initialise the network with system installed
  // networking software
  startbrowserdir[0] = '\0';
  netscapeneeded = 0;
  int err = 1;
  if (init_type == 1) err = tryinitnetwork (Instance, MsgWindow, NULL);
  
  // if an error occurred, try again with billsock
  if (err != 0) {
    // get the path of billsock (current directory + ("net16" | "net32"))
    char winsockpath[1024]; // HARD LIMIT!!!!
    
    find_location();
    char *data_location_c = data_location.getcstr();
    strcpy (winsockpath, data_location_c);
    delete []data_location_c;
    
    // remove all the slashes at the end of billpath
    int len = strlen (winsockpath);
    while ((len > 0) && ((winsockpath[len-1] == '\\') || (winsockpath[len-1] == '/'))) {
      --len;
    }
    winsockpath[len] = '\0';
    
    if (osver.dwPlatformId == VER_PLATFORM_WIN32s)
      strcat (winsockpath, "\\net16\\");
    else {
      strcat (winsockpath, "\\net32\\");
      strcpy (startbrowserdir, winsockpath);
    }
    
    // try again
    err = tryinitnetwork (Instance, MsgWindow, winsockpath);
    
    if (err == 0) {
      // we will need to use netscape on 95/98/NT machines
      if (osver.dwPlatformId != VER_PLATFORM_WIN32s) {
	netscapeneeded = 1;
	// make sure the browser state is in step with the network condition
	gsdl_check_browser_settings (netscapeneeded);
      }
      
      // check to see if a browser is running
      if (browserrunning(gsdl_browser_exe) == NO_ERROR) {
	MessageBox (NULL, 
		    "The Greenstone Digital Library software has detected a\n"
		    "running browser. To let our networking software\n"
		    "correctly initialize, please shut down your browser\n"
		    "and restart the Greenstone Digital Library software.",
		    "Greenstone Digital Library Software", MB_OK|MB_APPLMODAL);
	PostMessage(MsgWindow,WM_CLOSE,0,0);
	finish_up(false); // will unload winsock
	exit(0);      // nothing more to do
      }
    }
  }
  
  // if an error occurred display a nice error message
  if (err != 0) {
    
    // WSASYSNOTREADY	Couldn't load winsock
    // WSAVERNOTSUPPORTED	The version of Windows Sockets support requested 
    //			is not provided by this particular Windows Sockets implementation.
    // WSAEINVAL	The Windows Sockets version specified by the application 
    //			is not supported by this DLL.
    // WSAEPROTONOSUPPORT	The specified protocol is not supported.
    // WSAEPROTOTYPE	The specified protocol is the wrong type for this socket.
    // WSAESOCKTNOSUPPORT	The specified socket type is not supported in 
    //		this address family.
    // WSANOTINITIALISED	A successful WSAStartup must occur before 
    //		using this function.
    // WSAENETDOWN	The Windows Sockets implementation 
    //		has detected that the network subsystem has failed.
    // WSAEADDRINUSE 	The specified address 
    //		is already in use. (See the SO_REUSEADDR socket option under setsockopt.)
    // WSAEFAULT	The namelen argument is too small (less 
    //		than the size of a struct sockaddr).
    // WSAEINPROGRESS	A blocking Windows Sockets call is in progress.
    // WSAEAFNOSUPPORT	The specified address family is not supported 
    //		by this protocol.
    // WSAENOBUFS	Not enough buffers available, too many connections.
    // WSAENOTSOCK	The descriptor is not a socket.
    // WSAEISCONN	The socket is already connected.
    // WSAEMFILE	No more file descriptors are available.
    // WSAEOPNOTSUPP	The referenced socket is not of a type that 
    //		supports the listen operation.
    
    // get a text version of the error number
    char errstr[256];
    
    switch (err) {
    case WSASYSNOTREADY: strcpy(errstr, "WSASYSNOTREADY"); 
      break;
    case WSAVERNOTSUPPORTED: strcpy (errstr, "WSAVERNOTSUPPORTED");
      break;
    case WSAEINVAL: strcpy (errstr, "WSAEINVAL");
      break;
    case WSAEPROTONOSUPPORT: strcpy (errstr, "WSAEPROTONOSUPPORT");
      break;
    case WSAEPROTOTYPE: strcpy (errstr, "WSAEPROTOTYPE");
      break;
    case WSAESOCKTNOSUPPORT: strcpy (errstr, "WSAESOCKTNOSUPPORT");
      break;
    case WSANOTINITIALISED: strcpy (errstr, "WSANOTINITIALISED");
      break;
    case WSAEFAULT: strcpy (errstr, "WSAEFAULT");
      break;
    case WSAEAFNOSUPPORT: strcpy (errstr, "WSAEAFNOSUPPORT");
      break;
    case WSAENOTSOCK: strcpy (errstr, "WSAENOTSOCK");
      break;
    case WSAEISCONN: strcpy (errstr, "WSAEISCONN");
      break;
    case WSAEOPNOTSUPP: strcpy (errstr, "WSAEOPNOTSUPP");
      break;
    case WSAENETDOWN: strcpy (errstr, "WSAENETDOWN");
      break;
    case WSAEADDRINUSE: strcpy (errstr, "WSAEADDRINUSE");
      break;
    case WSAEINPROGRESS: strcpy (errstr, "WSAEINPROGRESS");
      break;
    case WSAENOBUFS: strcpy (errstr, "WSAENOBUFS");
      break;
    case WSAEMFILE: strcpy (errstr, "WSAEMFILE");
      break;
    default: sprintf (errstr, "WSA ERROR: %i", err);
      break;
    }
    
    // create a nice error message
    char message[2048];
    
    switch (err) {
    case WSAEADDRINUSE:
      // couldn't bind socket
      sprintf (message, "Could not find a free socket.", errstr);
      break;
      
    default:
      // cannot init winsock
      sprintf (message, "Could not initialize the network\n"
	       " (Reason: %s).", errstr);
      break;
    }
    
    strcat (message, "\n\n"
	    "Please submit a bug report using the support.htm file\n"
	    "on the CD-ROM.");
    
    MessageBox (NULL, message, "Greenstone Digital Library Software", MB_OK);
  }
  
  return (err == 0);
}


void log_computer_info () {
  char tmpstr[1024];
  
  // get various information about this computer
  if (gsdl_keep_log || gsdl_show_console) {
    // get operating system information
    OSVERSIONINFO osver;
    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    
    if (GetVersionEx(&osver)) {
      if (osver.dwPlatformId == VER_PLATFORM_WIN32s) {
	log_message ("Platform: win32s\n");
      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
	log_message ("Platform: windows 95\n");
      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
	log_message ("Platform: windows NT\n");
      }
      
      sprintf (tmpstr, "MajorVersion: %i\n", (int)osver.dwMajorVersion);
      log_message(tmpstr);
      sprintf (tmpstr, "MinorVersion: %i\n", (int)osver.dwMinorVersion);
      log_message(tmpstr);
      sprintf (tmpstr, "BuildNumber: %i\n", (int)osver.dwBuildNumber);
      log_message(tmpstr);
      sprintf (tmpstr, "CSDVersion: %s\n\n", osver.szCSDVersion);
      log_message(tmpstr);
    }
    
    // get memory information
    MEMORYSTATUS memstatus;
    memstatus.dwLength = sizeof(MEMORYSTATUS);
    GlobalMemoryStatus(&memstatus);
    
    sprintf (tmpstr, "TotalPhys: %i Meg\n", (int)(memstatus.dwTotalPhys/1024/1024));
    log_message (tmpstr);
    sprintf (tmpstr, "AvailPhys: %i Meg\n", (int)(memstatus.dwAvailPhys/1024/1024));
    log_message (tmpstr);
    sprintf (tmpstr, "TotalPageFile: %i Meg\n", (int)(memstatus.dwTotalPageFile/1024/1024));
    log_message (tmpstr);
    sprintf (tmpstr, "AvailPageFile: %i Meg\n", (int)(memstatus.dwAvailPageFile/1024/1024));
    log_message (tmpstr);
    sprintf (tmpstr, "TotalVirtual: %i Meg\n", (int)(memstatus.dwTotalVirtual/1024/1024));
    log_message (tmpstr);
    sprintf (tmpstr, "AvailVirtual: %i Meg\n\n", (int)(memstatus.dwAvailVirtual/1024/1024));
    log_message (tmpstr);
    
    // log the version number
    log_message("GSDL version: " GSDL_VERSION "\n");
  }
  
}

// possible command line options are 
// --location=directory (the path to GSDLHOME which defaults to the
// directory where server.exe lives)
// --mode=gli (when server.exe is run from GLI). If GLI, "gli." is 
// the prefix used to load autoenter and start_browser properties
// (gli.autoenter, gli.start_browser) from the config file
// --config=file (the path to the configuration file to use which defaults
// to GSDLHOME\llssite.cfg)
// --cmd=init  Used to initialize the llssite.cfg file only - jmt12 18/11/2003
static void parse_args(const text_t cmdline, text_t &location, text_t &mode, text_t &config_file, text_t &command) {

  location.clear();
  config_file.clear();
  mode.clear();
  command.clear(); // jmt12 18/11/2003

  if (cmdline.size() < 4) return;

  text_t name, val;
  bool foundname = false;
  text_t::const_iterator here = cmdline.begin();
  text_t::const_iterator end = cmdline.end();
  while (here != end) {
    if (*here == '-' && ((here+1) != end) && (*(here+1) == '-')) {
      ++here;
      if (name == "location") {
	location = val;
      } else if(name == "mode") {
	mode = val;
      } else if (name == "config") {
	config_file = val;
      } else if(name == "cmd") { // jmt12 18/11/2003
	command = val;
      }
      foundname = false;
      name.clear();
      val.clear();
    } else if (*here == '=') {
      foundname = true;
    } else {
      if (foundname) {
	val.push_back(*here);
      } else {
	name.push_back(*here);
      }
    }
    ++here;
  }
  if (name == "location") {
    location = val;
  } else if (name == "mode") {
    mode = val;
  } else if (name == "config") {
    config_file = val;
  } else if(name == "cmd") { // jmt12 18/11/2003
    command = val;
  }
}


// Tries to generate llssite.cfg from llssite.cfg.in in directory given
// by location variable (assumes location is GSDLHOME), if llssite.cfg is missing
void ensure_llssite_cfg(const text_t location) {
  text_t configfile = location+"\\llssite.cfg";
  text_t configtemplate = location+"\\llssite.cfg.in";

  if(!file_exists (configfile)) {
    
    if(file_exists (configtemplate)) {
      // try creating llssite.cfg from the template configfile llssite.cfg.in
      // by using a straightforward copy
      file_copy(configtemplate, configfile);
    }
  }
}


// Added code to allow for an 'init' run of the server which just creates the llssite.cfg file
int __stdcall WinMain(HINSTANCE Instance, HINSTANCE /*PrevInstance*/, LPSTR CmdLineStr, int /*CmdShow*/) {
  HWND MainWindow;  MSG Message;  WNDCLASS MainClass;  

  // One instance of the local library server is quite enough, thank you very much
  HWND gw = FindWindow("Greenstone Digital Library Software",
		       "Greenstone Digital Library Software");
  if (gw != NULL) {
    exit(1);
  }

  // parse arguments
  text_t location, mode, config_file, command;
  parse_args(CmdLineStr, location, mode, config_file, command);
  gsdl_conffile = config_file;
  if (mode.empty()) {
	// if the server is run on its own, mode_prefix is "" with no '.' suffix
	gsdl_mode_property_prefix = "";
  } else {
	// if the server is launched from GLI, the mode_prefix is "gli" and needs
	// '.' suffixed when accessing certain properties from the config file.		
	gsdl_mode_property_prefix = trim(mode) + ".";
  }
 
  // Try to generate the llssite.cfg file if it's missing
  ensure_llssite_cfg(location);

  // jmt12 18/11/2003
  if(command == "init") {
    // init various modules
    read_settings(0); // don't know if netscape is needed at this point
    finish_up(true);
    exit(0);
  }

  //Create a window class
  MainClass.style = CS_HREDRAW | CS_VREDRAW;
  MainClass.lpfnWndProc = WndProc;
  MainClass.cbClsExtra = 0;
  MainClass.cbWndExtra = 0;
  MainClass.hInstance = Instance;
  MainClass.hIcon     = LoadIcon(Instance, MAKEINTRESOURCE(TRAY_ICON));
  MainClass.hCursor   = LoadCursor(NULL, IDC_ARROW);
  MainClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  MainClass.lpszMenuName = MAKEINTRESOURCE(Main_Menu);
  MainClass.lpszClassName = "Greenstone Digital Library Software";
  if (!RegisterClass(&MainClass))
    return 0;
  
  
  // Load various bitmaps
  coltitlebitmap = LoadBitmap (Instance, MAKEINTRESOURCE(GSDL_COL_TITLE));
  logobitmap= LoadBitmap (Instance, MAKEINTRESOURCE(GSDL_LOGO));
  
  // Create the main window
  MainWindow = CreateWindow("Greenstone Digital Library Software", 
			    "Greenstone Digital Library Software",
			    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
			    WS_MINIMIZEBOX,
			    CW_USEDEFAULT, CW_USEDEFAULT, 
			    MAINWINDOWWIDTH + WASTEWIDTH, 
			    MAINWINDOWHEIGHT + WASTEHEIGHT,
			    NULL, NULL, Instance, NULL);
  
  text_rect.left = 0;  
  text_rect.top = VERSIONY+VERSIONHEIGHT + 5;
  text_rect.right = MAINWINDOWWIDTH;
  text_rect.bottom = LOGOY-SPACERWIDTH;
  
  window_state = undefined_window;
  
  ShowWindow(MainWindow, SW_SHOW); 
  set_location(location);
  
  // get ready to draw the main window
  RECT windowRect;
  GetClientRect (MainWindow, &windowRect);
  HDC pdc = GetDC (MainWindow);
  
  // retrieve and save the text metrics
  TEXTMETRIC tm;
  GetTextMetrics(pdc, &tm);
  line_spacing = tm.tmHeight + tm.tmExternalLeading;
  GSDL_Window = MainWindow;  

  // init various modules
  read_settings (0); // don't know if netscape is needed at this point
  
  if (!gsdl_auto_enter) {
    
    // add the select version checkbox
    Enter = CreateWindow("BUTTON",             // predefined class 
			 "Enter Library",      // button text 
			 WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_DEFPUSHBUTTON,  // styles 
			 ENTERBUTTONX,         // starting x position 
			 ENTERBUTTONY,         // starting y position 
			 ENTERBUTTONWIDTH,     // button width 
			 ENTERBUTTONHEIGHT,    // button height 
			 MainWindow,           // parent window 
			 NULL,                 // No menu 
			 Instance, 
			 NULL);                // pointer not needed 
    
//      EnterRestricted = CreateWindow("Button",                // predefined class 
//  				   "Restricted Version",    // button text 
//  				   WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_PUSHBUTTON,   // styles 
//  				   ALTERNATIVEBUTTONX,      // starting x position 
//  				   ALTERNATIVEBUTTONY,      // starting y position 
//  				   ALTERNATIVEBUTTONWIDTH-(INFOBUTTONWIDTH+SPACERWIDTH),  // button width 
//  				   ALTERNATIVEBUTTONHEIGHT, // button height 
//  				   MainWindow,              // parent window 
//  				   (HMENU)NULL,             // No menu 
//  				   Instance, 
//  				   NULL);                   // pointer not needed 
    
//      InfoRestricted = CreateWindow("Button",                // predefined class 
//  				  NULL,
//  				  WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_BITMAP,   // styles 
//  				  ALTERNATIVEBUTTONWIDTH-INFOBUTTONWIDTH+SPACERWIDTH, // starting x position 
//  				  ALTERNATIVEBUTTONY,      // starting y position 
//  				  INFOBUTTONWIDTH,         // button width 
//  				  ALTERNATIVEBUTTONHEIGHT, // button height 
//  				  MainWindow,              // parent window 
//  				  (HMENU)NULL,             // No menu 
//  				  Instance, 
//  				  NULL);                   // pointer not needed 
    
//      HANDLE i_icon = LoadImage (Instance, MAKEINTRESOURCE(GSDL_ICBMP), IMAGE_BITMAP, 
//  			       INFOBUTTONWIDTH-4, ALTERNATIVEBUTTONHEIGHT-4, LR_DEFAULTCOLOR);
//      SendMessage (InfoRestricted, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)i_icon);
    
    SetFocus (Enter);
        
    // Since we have not entered the library yet, set the URL in the configfile to pending state
    gsdl_url = "URL_pending";
    write_settings(gsdl_url);

    enterstring = strenterlib;
    paint_window (pdc, windowRect);
    
    // message loop for init buttons
    while (!init_done) {
      if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
		if (Message.message == WM_QUIT) return Message.wParam;
		if (!IsDialogMessage (MainWindow, &Message)) {
			TranslateMessage(&Message); /* translate keyboard messages */
			DispatchMessage(&Message);  /* return control to Windows NT */
		}
      } else {
		Sleep (1);
      }
    }
    
    // don't want these buttons anymore
    DestroyWindow (Enter);
    // DestroyWindow (EnterRestricted);
    // DestroyWindow (InfoRestricted);
    
    enterstring = strnothing;
    paint_window (pdc, windowRect);
    
  } else {
    // auto enter is enabled - start up standard version
    init_type = 1;
    init_done = 1;
  }
  
  // draw the main window containing an init message
  infostring = strinit;
  paint_window (pdc, windowRect);
  DWORD lastcheck = GetTickCount();
  
  have_networking = initnetwork (Instance, MainWindow);
  if (!have_networking) {
    MessageBox(NULL,
	       "Failed to initialize networking.",
	       "Greenstone Digital Library Software",MB_OK|MB_SYSTEMMODAL);	
    exit (0);
  }

  if (!gsdl_init()) // quit if can't initialise the library correctly
    exit (0);
  initstartbrowser();
  log_computer_info ();
  
  // show the initialising message for at least 1 second
  while (DiffTickCounts (lastcheck, GetTickCount()) < 1000) {
    Sleep (1);
  }
  
  // attempt to start a browser
  if (gsdl_show_console || !gsdl_start_browser) infostring = strnothing;
  else infostring = strsb;
  paint_window (pdc, windowRect);
  lastcheck = GetTickCount();
  if (enter_library (MainWindow) && !gsdl_show_console) {
    // stay maximised for at least 1 second
    while (DiffTickCounts (lastcheck, GetTickCount()) < 1000) Sleep (1);
    ShowWindow(MainWindow,SW_MINIMIZE);
  }
  
  // add the "restart library" button
  GoButton = CreateWindow("BUTTON",              // predefined class 
			  "Restart Library",     // button text 
			  WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // styles 
			  RESTARTBUTTONX,        // starting x position 
			  RESTARTBUTTONY,        // starting y position 
			  RESTARTBUTTONWIDTH,    // button width 
			  RESTARTBUTTONHEIGHT,   // button height 
			  MainWindow,            // parent window 
			  NULL,                  // No menu 
			  Instance, 
			  NULL);                 // pointer not needed 
  
  infostring = strrestartlib;
  paint_window (pdc, windowRect);
  
  // release the DC used to display the init messages
  ReleaseDC (MainWindow, pdc);
  
  //Message loop
  lastcheck = GetTickCount();
  int seenbrowser = 0;	// if we see a browser then it disappears
                        // we will ask if they want to close the
                        // the library down
  char last_gsdl_browser_exe[MAX_FILENAME_SIZE]; // need to notice when the
                                                 // browser setting changes
  strcpy (last_gsdl_browser_exe, gsdl_browser_exe);
  
  for (;;) {
    if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {	
		if (Message.message == WM_QUIT) {
			break;
		}
		
		if (Message.message == WM_RESTART) {
			
			read_settings(0);
			if (!gsdl_init(false)) { // do the same as when restart library is pressed
				MessageBox(NULL,"Error re-initializing gsdl: gsdl_init()", 
					"Error initialising GSDL", MB_OK|MB_SYSTEMMODAL);
				// when Restart Library is pressed, exit(0) is called) but a break here
				// will exit Greenstone same as with WM_QUIT msg above
				break;
			}			
		}
		
		if (!IsDialogMessage (MainWindow, &Message)) {
			TranslateMessage(&Message); /* translate keyboard messages */
			DispatchMessage(&Message);  /* return control to Windows NT */
		}  
    } else {
      if (DiffTickCounts (lastcheck, GetTickCount()) > 500) {
	// make sure the browser hasn't changed
	if (strcmp (last_gsdl_browser_exe, gsdl_browser_exe) != 0) {
	  seenbrowser = 0;
	  strcpy (last_gsdl_browser_exe, gsdl_browser_exe);
	}
	
	// do check
	lastcheck = GetTickCount();
	if (gsdl_start_browser && browserrunning(gsdl_browser_exe) == NO_ERROR) {
	  // we were able to connect to a browser
	  seenbrowser = 1;
	  
	  // see if the enter library button has been pressed
	  // with nothing happening for 20 seconds
	  if ((enterlib_libaccessnum >= 0) && 
	      (enterlib_libaccessnum == libaccessnum) &&
	      (DiffTickCounts (enterlib_time, GetTickCount()) > 20000)) {
	    // something could be wrong!! (most likely cause is a proxy)
	    MessageBox(NULL,
		       "Your browser does not seem to be responding to the 'Enter Library'\n"
		       "request. Try turning off all proxy servers in your browser settings.\n"
		       "If you are using Internet Explorer make sure BOTH dialup settings\n"
		       "AND LAN settings have their proxy servers disabled.",
		       "Greenstone Digital Library Software",MB_OK|MB_SYSTEMMODAL);
	    enterlib_libaccessnum = -1;
	  }
	  
	  
	} else {
	  // no browser was found
	  
	  if (seenbrowser) {
	    // we have seen a browser in the past
	    if (MessageBox(NULL,
			   "The Greenstone Digital Library software has noticed that you\n"
			   "shut down your browser. Would you also like to shut down the\n"
			   "Greenstone Digital Library software?",
			   "Greenstone Digital Library Software",MB_YESNO|MB_SYSTEMMODAL) == IDYES)
	      PostMessage(MainWindow,WM_CLOSE,0,0);
	  }
	  seenbrowser = 0;
	}
      }
      
      Sleep (1); // millisecond
    }
  }
  
  return Message.wParam;
}

