#include "flock.hxx"
// #include <resource.h>
#include <cache.hxx>

#undef inet_ntoa
#undef inet_addr
#undef gethostname
#undef gethostbyname
#undef gethostbyaddr

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#ifdef sunos5
extern "C" int gethostname(char*,int);
#endif

extern HANDLE MwOpenProcess(pid_t, BOOL);
extern "C" MwAtExit(void (*f)(void));

//locally used functions
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len);
off_t lock_test(int fd, int type, off_t *offset, int whence, off_t *len);

#define REG_READONLYCACHE TEXT("Software\\Microsoft\\Internet Explorer\\Unix\\ReadOnlyCacheWarning")
#define REG_READONLYCACHEKEY TEXT("ShowCacheWarning")
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#define SECONDSINDAY 86400

#define REG_READONLYCACHE TEXT("Software\\Microsoft\\Internet Explorer\\Unix\\ReadOnlyCacheWarning")
#define REG_READONLYCACHEKEY TEXT("ShowCacheWarning")
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#define SECONDSINDAY 86400


// lock region relative to whence starting at offset upto len bytes
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) {

  struct flock lock;

  lock.l_type = type;       //F_RDLCK, F_WRLCK, F_UNLCK     
  lock.l_start = offset;    //byte offset, relative to l_whence
  lock.l_whence = whence;   //SEEK_SET, SEEK_CUR, SEEK_END  
  lock.l_len = len;     //#bytes (0 means to EOF)

  return( fcntl(fd, cmd, &lock) );
}

// test region for locks relative to whence starting at offset for len bytes
off_t lock_test(int fd, int type, off_t *offset, int whence, off_t *len) {

  struct flock lock;

  lock.l_type = type;       //F_RDLCK, F_WRLCK, F_UNLCK 
  lock.l_start = *offset;   //byte offset, relative to l_whence     
  lock.l_whence = whence;   //SEEK_SET, SEEK_CUR, SEEK_END  
  lock.l_len = *len;        // #bytes (0 means to EOF)      

  if (fcntl(fd, F_GETLK, &lock) < 0)
    return(-1);

  if (lock.l_type == F_UNLCK)
    return(0);          // nobody has lock in this region
  else if (lock.l_type == F_RDLCK) {
    *offset = lock.l_start;
    *len = lock.l_len;
    return(lock.l_start);   // byte offset of host with read lock
  } else {          // dont support extended semantics of
    return(-1);         // write lock yet
  }
}


extern "C" void unixCleanupWininetCacheLockFile()
{
//    if(!g_ReadOnlyCaches)
        //unlink(szLockDBName);
}

BOOL CreateAtomicCacheLockFile(BOOL *pfReadOnlyCaches, char **pszLockingHost)
{
    int fdlockdbf, fdlock, envLen, hostbynameerr;
    off_t IPOffset=0, IPLen=0, ownIPOffset, ownIPLen;
    char *hostname, hostbynamebuf[512];
    char szLockFileName[MAX_PATH+1], szLockDBName[MAX_PATH+1];
    struct hostent hostbynameresult;
#ifdef ux10
    struct hostent_data hostentdata;
#endif

    char *pEnv = getenv("MWUSER_DIRECTORY");

    /* Don't process the ielock file for Mainwin Lite programs */
    if (MwIsInitLite())
       goto Cleanup;

    if (pEnv == 0)
    return FALSE;

    envLen = strlen(pEnv);
    if (envLen > MAX_PATH-256)
        return FALSE;

    strcpy(szLockFileName, pEnv);
    if (szLockFileName[envLen-1] != '/') {
      szLockFileName[envLen] = '/';
      szLockFileName[envLen+1] = 0x00;
    }
    strcpy(szLockDBName, pEnv);
    if (szLockDBName[envLen-1] != '/') {
      szLockDBName[envLen] = '/';
      szLockDBName[envLen+1] = 0x00;
    }
    strcat(szLockFileName, LF);
    strcat(szLockDBName, LOCKDBF);

    hostname = (char *)malloc(256*sizeof(char));
    if ((hostname == NULL) || (gethostname(hostname, 256) == -1)) {
      *pfReadOnlyCaches = TRUE;
      return FALSE;
    }

#ifdef sunos5
    if (!(gethostbyname_r(hostname, &hostbynameresult, hostbynamebuf,
                 sizeof(hostbynamebuf), &hostbynameerr))) {
      *pfReadOnlyCaches = TRUE;
      return FALSE;
    }
#endif
#ifdef ux10
    if (gethostbyname_r(hostname, &hostbynameresult, &hostentdata) < 0) {
      *pfReadOnlyCaches = TRUE;
      return FALSE;
    }
#endif
    struct in_addr *ptr = (struct in_addr *)*hostbynameresult.h_addr_list;
    ownIPOffset = inet_netof(*ptr);
    ownIPLen = inet_lnaof(*ptr);

    if ((fdlock = open(szLockFileName, O_WRONLY|O_CREAT|O_EXCL, FILE_MODE)) < 0) {
      if (errno == EEXIST) {
        if ((fdlock = open(szLockFileName, O_WRONLY)) < 0) {
          *pfReadOnlyCaches = TRUE;
          return FALSE;
        }
      } else {
        *pfReadOnlyCaches = TRUE;
        return FALSE;
      }
    }

    if (writew_lock(fdlock, 0, SEEK_SET, 0) < 0) {
      *pfReadOnlyCaches = TRUE;
      return FALSE;
    }

    /*under this lock, now do all the examination of szLockDBName*/
    if ((fdlockdbf = open(szLockDBName, O_RDWR|O_CREAT|O_EXCL, FILE_MODE)) < 0) {
      if (errno == EEXIST) {
        if ((fdlockdbf = open(szLockDBName, O_RDWR)) < 0) {
          *pfReadOnlyCaches = TRUE;
          un_lock(fdlock, 0, SEEK_SET, 0);
          return FALSE;
        }
      } else {
        *pfReadOnlyCaches = TRUE;
        un_lock(fdlock, 0, SEEK_SET, 0);
        return FALSE;
      }
    }

    /* check entire file for locking */
    if ((can_writelock(fdlockdbf, &IPOffset, SEEK_SET, &IPLen)) >= 0) {
      if ((IPOffset == 0) || ((IPOffset == ownIPOffset) && (IPLen == ownIPLen))){
    // either no IE writing to cache or IE on own host writing to cache
    // (IP address is identical)..either way we have write access
    *pfReadOnlyCaches = FALSE;
    *pszLockingHost = hostname;
    //lock at "network part" position for "host part" bytes
    read_lock(fdlockdbf, ownIPOffset, SEEK_SET, ownIPLen);
    un_lock(fdlock, 0, SEEK_SET, 0);
    return TRUE;
      } else {
    //some other host writing to cache
        *pfReadOnlyCaches = TRUE;
        u_long addr = inet_addr(inet_ntoa(inet_makeaddr(IPOffset, IPLen)));
        struct hostent * hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
        if (!hp)
          ;       //cant find hostname from offset & length of locked bytes..
        else
          *pszLockingHost = hp->h_name;
        un_lock(fdlock, 0, SEEK_SET, 0);
        return TRUE;
      }
    } else {
      //can_writelock returned -1 with some fcntl error
      *pfReadOnlyCaches = TRUE;
      un_lock(fdlock, 0, SEEK_SET, 0);
      return FALSE;
    }

Cleanup:
    return TRUE;
}

BOOL DeleteAtomicCacheLockFile()
{
    /* Don't process for MainWin Lite programs */
    /* Right now, the code below does not make sense because all
     * we do is return TRUE. So, commenting out this code for now.
     */
#if 0
    if (MwIsInitLite())
       goto Cleanup;

Cleanup:
#endif /* 0 */
    //unlink(szLockDBName);
    return TRUE;
}

#if 0 // Back out till we get a consensus on this

BOOL CALLBACK ReadOnlyCache_DlgProc(HWND   hDlg,
                                    UINT   uMsg,
                                    WPARAM wParam,
                                    LPARAM lParam) {
     switch (uMsg) {
            case WM_INITDIALOG:
            {
                 LPTSTR lpszMessageStr = (LPTSTR)lParam;
                 TCHAR  pszText[MAX_PATH];
                 TCHAR  pszFormattedText[MAX_PATH];

                 if (lpszMessageStr)
                 {
                    if (LoadString(GlobalDllHandle,
                                   IDS_READONLYCACHE,
                                   pszText,
                                   ARRAYSIZE(pszText))) {
                       wsprintf(pszFormattedText,pszText, lpszMessageStr);
                       SetDlgItemText(hDlg, IDC_READONLYCACHE, pszFormattedText);
                    }
                 }

                 SetFocus(GetDlgItem(hDlg, IDOK));
            }
            break;

            case WM_COMMAND:
                 switch (LOWORD(wParam))
                 {
                        case IDOK:
                        {
                             if (IsDlgButtonChecked(hDlg, IDC_DONT_WANT_WARNING))
                                EndDialog(hDlg, 1);
                             else
                                EndDialog(hDlg, 0);
                             break;
                        }

                        default:
                             return FALSE;
                 }
                 return TRUE;
            case WM_CLOSE:
            {
                 if (IsDlgButtonChecked(hDlg, IDC_DONT_WANT_WARNING))
                    EndDialog(hDlg, 1);
                 else
                    EndDialog(hDlg, 0);
            }
            return TRUE;
     }

     return FALSE;
}

void ShowReadOnlyCacheDialog(char* pszHostName) {
     DWORD dwError = E_FAIL;
     HKEY  hKey = NULL;
     DWORD dwValue = 0;
     DWORD dwValueType;
     DWORD dwValueSize = sizeof(DWORD);

     if ((dwError = REGOPENKEYEX(HKEY_CURRENT_USER,
                            REG_READONLYCACHE,
                            0,
                            KEY_READ|KEY_WRITE,
                            &hKey)) != ERROR_SUCCESS)
     {
        goto Cleanup;
     }

     if ((dwError = RegQueryValueEx(hKey,
                               REG_READONLYCACHEKEY,
                               0,
                               &dwValueType,
                               (LPBYTE)&dwValue,
                               &dwValueSize)) != ERROR_SUCCESS)
     {
        goto Cleanup;
     }

     if (dwValue)
     {
        int fRet = 0;

        if ((fRet = DialogBoxParam(GlobalDllHandle,
                              MAKEINTRESOURCE(IDD_READONLYCACHE),
                              NULL,
                              ReadOnlyCache_DlgProc,
                              (LPARAM)pszHostName)) < 0)
        {
           goto Cleanup;
        }

        /*
         * we are here, because the registry told us to show this dialog.
         * now, we check if fRet == TRUE, in which case we don't show this
     * dialog in the future. And, we update the registry.
         */

        if (fRet == 1) {
           /* ShowCacheWarning will be set to False in the registry */
           dwValue = 0;

           /*
            * we don't check for the error here, because we close the key next
            * and if we did not save successfully, we will show this dialog again
            */

           RegSetValueEx(hKey,
                         REG_READONLYCACHEKEY,
                         0,
                         dwValueType,
                         (LPBYTE)&dwValue,
                         dwValueSize);
        }
     }

Cleanup:

     if (hKey)
        REGCLOSEKEY(hKey);

     return;
}
#endif