/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_extensions.hxx"

#ifdef OS2
#define UNIX
#endif

#ifdef UNIX

#define XP_UNIX 1

#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>

#endif //end of UNIX

#ifdef WNT

#define _WINDOWS

#pragma warning (push,1)
#pragma warning (disable:4668)

#include <windows.h>
#include <direct.h>
#include <stdlib.h>
#include <io.h>
#include <sys/types.h>
#include <fcntl.h>

#pragma warning (pop)

#endif //end of WNT


#include <stdio.h>

#include <string.h>
#include <errno.h>
#include "boost/scoped_array.hpp"
#include "tools/pathutils.hxx"

//http://qa.openoffice.org/issues/show_bug.cgi?id=82545
//https://bugzilla.mozilla.org/show_bug.cgi?id=241262
#ifdef UNIX
#  ifndef _UINT32
#    if defined(__alpha) || defined(__LP64__)
       typedef unsigned int uint32;
#    else  /* __alpha */
       typedef unsigned long uint32;
#    endif
#    define _UINT32
#  endif
#  ifndef _INT32
#    if defined(__alpha) || defined(__LP64__)
       typedef int int32;
#    else  /* __alpha */
       typedef long int32;
#    endif
#    define _INT32
#  endif
#endif

#include "npapi.h"
#include "npshell.hxx"
#include "so_env.hxx"
#include "so_msg.hxx"
#include "ns_debug.hxx"


#include "nsp_func.hxx"

#include <comphelper/documentconstants.hxx>

/***********************************************************************
 *
 * Implementations of plugin API functions
 *
 ***********************************************************************/

static NSP_Mute_Obj send_lock = NSP_New_Mute_Obj();
static NSP_PIPE_FD write_fd = 0;


long int NSP_WriteToPipe(NSP_PIPE_FD fp, void* buf, unsigned long int len)
{
    unsigned long int len_unix = 0, len_wnt = 0;

    len_unix = NSP_Write_Pipe(fp, buf, len, &len_wnt);
#ifdef UNIX
    (void)len_wnt;
    return  len_unix;
#endif //end of UNIX
#ifdef WNT
    return  len_wnt;
#endif //end of WNT

}

#ifdef UNIX
static pid_t nChildPID = 0;
#endif

#define MY_LENGTH(s) (sizeof (s) / sizeof *(s) - 1)
#define MY_STRING(s) (s), MY_LENGTH(s)

#if defined WNT
namespace {

bool extendEnvironment(
    boost::scoped_array< WCHAR > * environment, WCHAR const * pathBegin,
    WCHAR const * pathEnd)
{
    WCHAR ure[MAX_PATH];
    if (tools::buildPath(ure, pathBegin, pathEnd, MY_STRING(L"\\ure-link")) ==
        NULL)
    {
        return false;
    }
    WCHAR * ureEnd = tools::resolveLink(ure);
    if (ureEnd == NULL) {
        return false;
    }
    ureEnd = tools::buildPath(ure, ure, ureEnd, MY_STRING(L"\\bin"));
    if (ureEnd == NULL) {
        return false;
    }
    WCHAR const * env = GetEnvironmentStringsW();
    if (env == NULL) {
        return false;
    }
    WCHAR const * p = env;
    WCHAR const * p1 = NULL;
    while (*p != L'\0') {
        size_t n = wcslen(p);
        if (p1 == NULL && n >= MY_LENGTH(L"PATH=") &&
            (p[0] == L'P' ||  p[0] == L'p') &&
            (p[1] == L'A' ||  p[1] == L'a') &&
            (p[2] == L'T' ||  p[2] == L't') &&
            (p[3] == L'H' ||  p[3] == L'h') && p[4] == L'=')
        {
            p1 = p + MY_LENGTH(L"PATH=");
            //TODO: check whether the ure path is already present in PATH (at
            // the beginning of PATH?)
        }
        p += n + 1;
    }
    ++p;
    if (p1 == NULL) {
        environment->reset(
            new WCHAR[MY_LENGTH(L"PATH=") + (ureEnd - ure) + 1 + (p - env)]);
            //TODO: overflow
        memcpy(environment->get(), MY_STRING(L"PATH=") * sizeof (WCHAR));
        memcpy(
            environment->get() + MY_LENGTH(L"PATH="), ure,
            ((ureEnd - ure) + 1) * sizeof (WCHAR));
        memcpy(
            environment->get() + MY_LENGTH(L"PATH=") + (ureEnd - ure) + 1, env,
            (p - env) * sizeof (WCHAR));
    } else {
        environment->reset(
            new WCHAR[(p - env) + (ureEnd - ure) + MY_LENGTH(L";")]);
            //TODO: overflow
        memcpy(environment->get(), env, (p1 - env) * sizeof (WCHAR));
        memcpy(
            environment->get() + (p1 - env), ure,
            (ureEnd - ure) * sizeof (WCHAR));
        environment->get()[(p1 - env) + (ureEnd - ure)] = L';';
        memcpy(
            environment->get() + (p1 - env) + (ureEnd - ure) + 1, p1,
            (p - p1) * sizeof (WCHAR));
    }
    return true;
}

}
#endif

// start nspluin executable in child process, and use pipe to talk with it
int do_init_pipe()
{
    debug_fprintf(NSP_LOG_APPEND, "enter do_init_pipe 1\n");
    NSP_PIPE_FD fd[2];

    if( 0 != NSP_Inherited_Pipe(fd) )
        return NPERR_GENERIC_ERROR;

    write_fd = fd[1];   // write fd
#ifdef UNIX
    // the parent process will wait for the child process in NPP_Shutdown code
    nChildPID = fork();

    if( ! nChildPID )  // child process
#endif //end of UNIX
    {
#ifdef UNIX
        char s_read_fd[16] = {0};
        char s_write_fd[16] = {0};
        sprintf(s_read_fd,  "%d", fd[0]);
        sprintf(s_write_fd, "%d", fd[1]);
        char const * instdir = findInstallDir();
        boost::scoped_array< char > exepath(
            new char[strlen(instdir) +
                     RTL_CONSTASCII_LENGTH("/basis-link/program/nsplugin") +
                     1]);
        sprintf(exepath.get(), "%s/basis-link/program/nsplugin", instdir);
        char const * progdir = findProgramDir();
        boost::scoped_array< char > inifilepath(
            new char[
                RTL_CONSTASCII_LENGTH(
                    "-env:INIFILENAME=vnd.sun.star.pathname:") +
                strlen(progdir) + RTL_CONSTASCII_LENGTH("/redirectrc") + 1]);
            //TODO: overflow
        sprintf(
            inifilepath.get(),
            "-env:INIFILENAME=vnd.sun.star.pathname:%s/redirectrc", progdir);
        execl(
            exepath.get(), exepath.get(), s_read_fd, s_write_fd,
            inifilepath.get(), progdir, NULL);
        _exit(255);
#endif //end of UNIX
#ifdef WNT
        WCHAR s_read_fd[16] = {0};
        WCHAR s_write_fd[16] = {0};
        wsprintfW(s_read_fd, L"%d", fd[0]);
        wsprintfW(s_write_fd, L"%d", fd[1]);
        WCHAR brand[MAX_PATH];
        int brandLen = MultiByteToWideChar(
            CP_ACP, MB_PRECOMPOSED, findInstallDir(), -1, brand, MAX_PATH);
            //TODO: conversion errors
        if (brandLen == 0) {
            return NPERR_GENERIC_ERROR;
        }
        WCHAR path[MAX_PATH];
        if (tools::buildPath(
                path, brand, brand + brandLen - 1, MY_STRING(L"\\basis-link"))
            == NULL)
        {
            return NPERR_GENERIC_ERROR;
        }
        WCHAR * pathEnd = tools::resolveLink(path);
        if (pathEnd == NULL) {
            return NPERR_GENERIC_ERROR;
        }
        boost::scoped_array< WCHAR > env;
        if (!extendEnvironment(&env, path, pathEnd)) {
            return NPERR_GENERIC_ERROR;
        }
        pathEnd = tools::buildPath(
            path, path, pathEnd, MY_STRING(L"\\program"));
        if (pathEnd == NULL) {
            return NPERR_GENERIC_ERROR;
        }
        WCHAR exe[MAX_PATH];
        WCHAR * exeEnd = tools::buildPath(
            exe, path, pathEnd, MY_STRING(L"\\nsplugin.exe"));
        if (exeEnd == NULL) {
            return NPERR_GENERIC_ERROR;
        }
        WCHAR * brandEnd = tools::buildPath(
            brand, brand, brand + brandLen - 1,
            MY_STRING(L"\\program\\redirect.ini"));
        if (brandEnd == NULL) {
            return NPERR_GENERIC_ERROR;
        }
        boost::scoped_array< WCHAR > args(
            new WCHAR[
                MY_LENGTH(L"\"") + (exeEnd - exe) + MY_LENGTH(L"\" ") +
                wcslen(s_read_fd) + MY_LENGTH(L" ") + wcslen(s_write_fd) +
                MY_LENGTH(L" \"-env:INIFILENAME=vnd.sun.star.pathname:") +
                (brandEnd - brand) + MY_LENGTH(L"\"") + 1]); //TODO: overflow
        wsprintfW(
            args.get(),
            L"\"%s\" %s %s \"-env:INIFILENAME=vnd.sun.star.pathname:%s\"", exe,
            s_read_fd, s_write_fd, brand);
        STARTUPINFOW NSP_StarInfo;
        memset((void*) &NSP_StarInfo, 0, sizeof(STARTUPINFOW));
        NSP_StarInfo.cb = sizeof(STARTUPINFOW);
        PROCESS_INFORMATION NSP_ProcessInfo;
        memset((void*)&NSP_ProcessInfo, 0, sizeof(PROCESS_INFORMATION));
        if(!CreateProcessW(
               exe, args.get(), NULL, NULL, TRUE,
               CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, env.get(), path,
               &NSP_StarInfo, &NSP_ProcessInfo))
        {
            DWORD Err = GetLastError();
            (void)Err;
        }
#endif //end of WNT
    }
    NSP_Close_Pipe(fd[0]);
    return NPERR_NO_ERROR;
}

bool sendMsg( PLUGIN_MSG* pMsg, size_t len, int iEnsure)
{
    NSP_Lock_Mute_Obj(send_lock);
    size_t len_w = 0;

    debug_fprintf(NSP_LOG_APPEND, "try to send message type:%d; len: %d\n", pMsg->msg_id, len);
    /*
    debug_fprintf(NSP_LOG_APPEND, "NSPlugin Message: msg_id:%d; instance_id:%d;
        wnd_id:%d;wnd_x:%d;wnd_y:%d;wnd_w:%d;wnd_h:%d; url:%s\n",
        pMsg->msg_id, pMsg->instance_id, pMsg->wnd_id,
        pMsg->wnd_x, pMsg->wnd_y, pMsg->wnd_w, pMsg->wnd_h, pMsg->url);*/
    len_w = NSP_WriteToPipe(write_fd, (void*) pMsg, len);
    if (len_w != len){
        if(errno == EPIPE) // If pipe breaks, then init pipe again and resend the msg
        {
            if(iEnsure){
                debug_fprintf(NSP_LOG_APPEND, "send message error, plugin exec need to be restart\n");
                NSP_Close_Pipe(write_fd);
                do_init_pipe();
                len_w = NSP_WriteToPipe(write_fd, (void*) pMsg, len);
            }
        }
        else if(errno == EINTR) // If interrupted by signal, then continue to send
        {
            long  new_len;
            debug_fprintf(NSP_LOG_APPEND, "send message error, send intrrupted by singal, resend again\n");
            new_len = NSP_WriteToPipe(write_fd, (char*)pMsg+len_w, len-len_w);
            len_w = len_w + new_len;
        }
        else  // else return error
        {
            debug_fprintf(NSP_LOG_APPEND, "send message error :%s.\n", strerror(errno));
            len_w = 0;
        }
    }
    NSP_Unlock_Mute_Obj(send_lock);
    debug_fprintf(NSP_LOG_APPEND, "send message success!\n");
    return len_w == len;
}

extern "C"
{
char* pMimeTypes = const_cast< char* >( "application/vnd.stardivision.calc:sdc:StarCalc 3.0 - 5.0;"
"application/vnd.stardivision.chart:sds:StarChart 3.0 - 5.0;"
"application/vnd.stardivision.draw:sda:StarDraw 3.0 - 5.0;"
"application/vnd.stardivision.impress:sdd:StarImpress 3.0 - 5.0;"
"application/vnd.stardivision.impress-packed:sdp:StarImpress-packed 3.0 - 5.0;"
"application/vnd.stardivision.math:smf:StarMath 3.0 - 5.0;"
"application/vnd.stardivision.writer:vor:StarWriter Template 3.0 - 5.0;"
"application/vnd.stardivision.writer-global:sgl:StarWriter Global 3.0 - 5.0;"
"application/vnd.staroffice.writer:sdw:StarWriter 3.0 - 5.0;"
MIMETYPE_VND_SUN_XML_CALC_ASCII ":sxc:StarOffice 6.0/7 Spreadsheet;"
MIMETYPE_VND_SUN_XML_CALC_TEMPLATE_ASCII":stc:StarOffice 6.0/7 Spreadsheet Template;"
MIMETYPE_VND_SUN_XML_DRAW_ASCII ":sxd:StarOffice 6.0/7 Drawing;"
MIMETYPE_VND_SUN_XML_DRAW_TEMPLATE_ASCII ":std:StarOffice 6.0/7 Drawing Template;"
MIMETYPE_VND_SUN_XML_IMPRESS_ASCII ":sxi:StarOffice 6.0/7 Presentation;"
MIMETYPE_VND_SUN_XML_IMPRESS_TEMPLATE_ASCII ":sti:StarOffice 6.0/7 Presentation Template;"
MIMETYPE_VND_SUN_XML_MATH_ASCII ":sxm:StarOffice 6.0/7 Formula;"
MIMETYPE_VND_SUN_XML_WRITER_ASCII ":sxw:StarOffice 6.0/7 Text Document;"
MIMETYPE_VND_SUN_XML_WRITER_GLOBAL_ASCII ":sxg:StarOffice 6.0/7 Master Document;"
MIMETYPE_VND_SUN_XML_WRITER_TEMPLATE_ASCII ":stw:StarOffice 6.0/7 Text Document Template;"
MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII ":odt:OpenDocument Text;"
MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII ":ott:OpenDocument Text Template;"
MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII ":odm:OpenDocument Master Document;"
MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII ":oth:HTML Document Template;"
MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII ":ods:OpenDocument Spreadsheet;"
MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII ":ots:OpenDocument Spreadsheet Template;"
MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII ":odg:OpenDocument Drawing;"
MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII ":otg:OpenDocument Drawing Template;"
MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII ":odp:OpenDocument Presentation;"
MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII ":otp:OpenDocument Presentation Template;"
MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII ":odf:OpenDocument Formula;" );

char*
NPP_GetMIMEDescription(void)
{
    debug_fprintf(NSP_LOG_APPEND, "print by Netscape Plugin,  NPP_GetMIMEDescription:%s.\n", pMimeTypes);
    return(pMimeTypes);
}

#ifdef UNIX
NPError
// I am not actually sure wrt this, it ast least compiles with external
// npapi.h now...
NPP_GetValue(NPP /*instance*/, NPPVariable variable, void *value)
{
    NPError err = NPERR_NO_ERROR;

    switch (variable) {
        case NPPVpluginNameString:
            // add here, for dynamic productname
            *((char **)value) = NSP_getPluginName();
            break;
        case NPPVpluginDescriptionString:
            // add here, for dynamic product description
            *((char **)value) = NSP_getPluginDesc();
            break;
        default:
            err = NPERR_GENERIC_ERROR;
    }
    debug_fprintf(NSP_LOG_APPEND, "print by Netscape Plugin,  NPP_GetValue return %d.\n", err);
    return err;
}


NPMIMEType
dupMimeType(NPMIMEType type)
{
    NPMIMEType mimetype = (NPMIMEType) NPN_MemAlloc(strlen(type)+1);
    mimetype[strlen(type)] = 0;
    if (mimetype)
        strcpy(mimetype, type);
    return(mimetype);
}
#endif // end of UNIX

NPError
NPP_Initialize(void)
{
    debug_fprintf(NSP_LOG_NEW, "NS Plugin begin initialize.\n");
    return (NPError)do_init_pipe();
}

#ifdef OJI
jref
NPP_GetJavaClass()
{
    return NULL;
}
#endif

void
NPP_Shutdown(void)
{
    PLUGIN_MSG msg;
    memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
    msg.msg_id = SO_SHUTDOWN;
    sendMsg(&msg, sizeof(PLUGIN_MSG), 0);
    NSP_Close_Pipe(write_fd);

#ifdef UNIX
    // on Unix we should wait till the child process is dead
    int nStatus;
    waitpid( nChildPID, &nStatus, 0 );
#endif
}

NPError
NPP_New(NPMIMEType pluginType,
    NPP instance,
    uint16 mode,
    int16 /*argc*/,
    char* /*argn*/[],
    char* /*argv*/[],
    NPSavedData* /*saved*/)
{
    PluginInstance* This;

    debug_fprintf(NSP_LOG_APPEND, "print by Netscape Plugin, enter NPP_New.\n");
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    instance->pdata = (PluginInstance*)NPN_MemAlloc(sizeof(PluginInstance));

    memset(instance->pdata, 0 , sizeof(PluginInstance));
    This = (PluginInstance*) instance->pdata;

    if (This == NULL)
    {
        return NPERR_OUT_OF_MEMORY_ERROR;
    }

    memset(This, 0, sizeof(PluginInstance));

    /* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
#ifdef UNIX
    This->mode = mode;
    This->type = dupMimeType(pluginType);
    This->instance = instance;
    This->pluginsPageUrl = NULL;
    This->exists = FALSE;
#endif //end of UNIX
#ifdef WNT
    (void)pluginType;
    This->fWindow = (NPWindow*)NPN_MemAlloc(sizeof(NPWindow));
    memset(This->fWindow, 0, sizeof (NPWindow));
    This->fMode = mode;
  #endif //end of WNT
    PLUGIN_MSG msg;
    memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
    msg.msg_id = SO_NEW_INSTANCE;
    msg.instance_id = (plugin_Int32)instance;
    if (!sendMsg(&msg, sizeof(PLUGIN_MSG), 1))
        return NPERR_GENERIC_ERROR;

    NPN_Status(instance, "......");
    return NPERR_NO_ERROR;
}

NPError
NPP_Destroy(NPP instance, NPSavedData** /*save*/)
{
    debug_fprintf(NSP_LOG_APPEND, "print by Nsplugin, enter NPP_Destroy.\n");
    PluginInstance* This;

    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    // Send destroy message
    PLUGIN_MSG msg;
    memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
    msg.msg_id = SO_DESTROY;
    msg.instance_id = (plugin_Int32)instance;
#ifdef UNIX
    msg.wnd_id =(plugin_Int32)((PluginInstance*) instance->pdata)->window;
#endif //end of UNIX
#ifdef WNT
    msg.wnd_id =(plugin_Int32)((PluginInstance*) instance->pdata)->fhWnd;
#endif //end of WNT
    sendMsg(&msg, sizeof(PLUGIN_MSG), 0);

    // Free the instance space
    This = (PluginInstance*) instance->pdata;
    if (This != NULL) {
#ifdef UNIX
        if (This->type)
            NPN_MemFree(This->type);
        if (This->pluginsPageUrl)
            NPN_MemFree(This->pluginsPageUrl);
        if (This->pluginsFileUrl)
                NPN_MemFree(This->pluginsFileUrl);
#endif //end of UNIX
#ifdef WNT
        if(This->fWindow)
            NPN_MemFree(This->fWindow);
#endif //end of WNT
        NPN_MemFree(instance->pdata);
        instance->pdata = NULL;
    }

    return NPERR_NO_ERROR;
}


NPError
NPP_SetWindow(NPP instance, NPWindow* window)
{
    PluginInstance* This;
#ifdef UNIX
    NPSetWindowCallbackStruct *ws_info;
#endif //end of UNIX

    debug_fprintf(NSP_LOG_APPEND, "print by Netscape Plugin, received window resize.\n");
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    This = (PluginInstance*) instance->pdata;
    if (This == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    // Prepare the general part of the SET_WINDOW message
    PLUGIN_MSG msg;
    memset((char*)&msg, 0, sizeof(msg));
    msg.msg_id = SO_SET_WINDOW;
    msg.instance_id = (plugin_Int32)instance;

    if ( window )
    {
        // Set window info for instance
#ifdef UNIX
        ws_info        = (NPSetWindowCallbackStruct *)window->ws_info;
        This->window   = (Window) window->window;
        This->x        = window->x;
        This->y        = window->y;
        This->width    = window->width;
        This->height   = window->height;
        This->display  = ws_info->display;
        This->visual   = ws_info->visual;
        This->depth    = ws_info->depth;
        This->colormap = ws_info->colormap;
#endif    //end of UNIX
#ifdef WNT
        This->fhWnd   = (HWND) window->window;
        This->fWindow->x        = window->x;
        This->fWindow->y        = window->y;
        This->fWindow->width    = window->width;
        This->fWindow->height   = window->height;
#endif    //end of WNT
        debug_fprintf(NSP_LOG_APPEND, "begin Set window of Office\n");
        debug_fprintf(NSP_LOG_APPEND, "W=(%d) H=(%d)\n", window->width, window->height);

        // fill the window dependent part of the message
        msg.wnd_id = (plugin_Int32) window->window;
        msg.wnd_x = window->x;
        msg.wnd_y = window->y;
        msg.wnd_w = window->width;
        msg.wnd_h = window->height;
    }
    else
    {
        // empty window pointer usually means closing of the parent window
#ifdef UNIX
        ws_info        = NULL;
        This->window   = (Window) NULL;
        This->x        = 0;
        This->y        = 0;
        This->width    = 0;
        This->height   = 0;
        This->display  = NULL;
        This->visual   = NULL;
#endif    //end of UNIX
#ifdef WNT
        This->fhWnd   = (HWND) NULL;
        This->fWindow->x        = 0;
        This->fWindow->y        = 0;
        This->fWindow->width    = 0;
        This->fWindow->height   = 0;
#endif    //end of WNT
        debug_fprintf(NSP_LOG_APPEND, "Empty window pointer is provided\n");

        // fill the window dependent part of the message
        msg.wnd_id = (plugin_Int32) NULL;
        msg.wnd_x = 0;
        msg.wnd_y = 0;
        msg.wnd_w = 0;
        msg.wnd_h = 0;
    }

    if(!sendMsg(&msg, sizeof(PLUGIN_MSG), 1))
    {
        debug_fprintf(NSP_LOG_APPEND, "NPP_SetWindow return failure \n");
        return NPERR_GENERIC_ERROR;
    }

    return NPERR_NO_ERROR;
}


NPError
NPP_NewStream(NPP instance,
          NPMIMEType /*type*/,
          NPStream* /*stream*/,
          NPBool /*seekable*/,
          uint16 *stype)
{
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    // Notify Mozilla to fetch the remote file into local cache directory
    *stype=NP_ASFILEONLY;

    return NPERR_NO_ERROR;
}


int32 STREAMBUFSIZE = 0X0FFFFFFF;
/* If we are reading from a file in NPAsFile
 * mode so we can take any size stream in our
 * write call (since we ignore it) */

int32
NPP_WriteReady(NPP /*instance*/, NPStream* /*stream*/)
{
    return STREAMBUFSIZE;
}


int32
NPP_Write(NPP /*instance*/, NPStream* /*stream*/, int32 /*offset*/, int32 len, void* /*buffer*/)
{
    return len;     /* The number of bytes accepted */
}


NPError
NPP_DestroyStream(NPP instance, NPStream* /*stream*/, NPError /*reason*/)
{
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;
    return NPERR_NO_ERROR;
}

// save fname to another file with the original file name
void
NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
{
    debug_fprintf(NSP_LOG_APPEND, "Into Stream\n");
    char* url = (char*)stream->url;
    char filename[1024] = {0};
    char* pfilename = NULL;
    if (NULL != (pfilename = strrchr(url, '/')))
    {
        strcpy(filename, pfilename+1);
    } else {
        return;
    }

    int length = strlen(url);
    debug_fprintf(NSP_LOG_APPEND, "url: %s; length: %d\n", url, length);
    PluginInstance* This;
    This = (PluginInstance*) instance->pdata;

    debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile\n");

    // copy cached file to another file with original name
    char localPathNew[NPP_PATH_MAX] = {0};
    char localFileNew[NPP_PATH_MAX] = {0};
    // if the file is from local
    if (0 == STRNICMP(url, "file:///", strlen("file:///")))
    {
        sprintf(localPathNew, "%s", fname);
        char* pAskSymbol = NULL;
        if(NULL != (pAskSymbol = strrchr(localPathNew, '?')))
            *pAskSymbol = 0;
    }
    else // from network, on windows, fname is c:\abc123
    {
        strncpy(localPathNew, fname, sizeof(localPathNew));
        char* pRandomFilename = NULL;

#ifdef UNIX
        if(NULL != (pRandomFilename = strrchr(localPathNew, '/')))
#endif //end of UNIX
#ifdef WNT
        if(NULL != (pRandomFilename = strrchr(localPathNew, '\\')))
#endif //end of WNT
        {
            pRandomFilename[1] = 0;
        } else {
            return;
        }
        strcat(localPathNew, filename);
        char* pAskSymbol = NULL;
        if(NULL != (pAskSymbol = strrchr(localPathNew, '?')))
            *pAskSymbol = 0;

        sprintf(localFileNew, "file://%s", localPathNew);
        UnixToDosPath(localFileNew);
        debug_fprintf(NSP_LOG_APPEND, "fname: %s\n localPathNew: %s\nlocalFileNew: %s\n",
            fname, localPathNew, localFileNew);

        restoreUTF8(localPathNew);
        restoreUTF8(localFileNew);
        if(0 != strcmp(fname, localPathNew)) {

#ifdef WNT
        if(NULL == CopyFile(fname, localPathNew, FALSE))
            return;
#endif //end of WNT

#ifdef UNIX
        int fdSrc, fdDst;
        if((0 > (fdSrc = open(fname, O_RDONLY)))){
                return;
        }
        remove(localPathNew);
        umask(0);
        if  (0 > (fdDst = open(localPathNew, O_WRONLY|O_CREAT,
                        S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))){
            close( fdSrc);
            debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile:can not create cache file %s. error: %s \n",
                localPathNew, strerror(errno));
            return;
        }
        char buffer[NPP_BUFFER_SIZE] = {0};
        int ret;
        while(0 <= (ret = read(fdSrc, buffer, NPP_BUFFER_SIZE)))
        {
            if (0 == ret)
            {
                if(EINTR == errno)
                    continue;
                else

                    break;
            }
            write(fdDst, buffer, ret);
        }
        close(fdSrc);
        close(fdDst);
#endif //end of UNIX

        debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile:before SetURL\n");
        }
    }

    // send SO_SET_URl message to inform the new URL
    PLUGIN_MSG msg;
    memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
    msg.msg_id = SO_SET_URL;
    msg.instance_id = (plugin_Int32)instance;
#ifdef UNIX
    msg.wnd_id =(plugin_Int32)(This->window);
    sprintf(msg.url, "file://%s", localPathNew);
#endif //end of UNIX
#ifdef WNT
    msg.wnd_id =(int)(This->fhWnd);
    sprintf(msg.url, "file:///%s", localPathNew);
    DosToUnixPath(msg.url);
#endif //endof WNT
    if(!sendMsg(&msg, sizeof(PLUGIN_MSG), 1))
        debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile send SO_SET_URL return failure \n");

    // send SO_SET_WINDOW message
//    memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
    msg.msg_id = SO_SET_WINDOW;
    msg.instance_id = (plugin_Int32)instance;
//  msg.wnd_id =(plugin_Int32)((PluginInstance*) instance->pdata)->window;
#ifdef UNIX
    msg.wnd_x = This->x;
    msg.wnd_y = This->y;
    msg.wnd_w = This->width;
    msg.wnd_h = This->height;
#endif //end of UNIX
#ifdef WNT
    msg.wnd_x = This->fWindow->x;
    msg.wnd_y = This->fWindow->y;
    msg.wnd_w = This->fWindow->width;
    msg.wnd_h = This->fWindow->height;
#endif //endof WNT
    if(!sendMsg(&msg, sizeof(PLUGIN_MSG), 1))
        debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile send SO_SET_WINDOW return failure \n");
}

void
NPP_URLNotify(NPP /*instance*/, const char* /*url*/,
                NPReason /*reason*/, void* /*notifyData*/)
{
}


void
NPP_Print(NPP instance, NPPrint* printInfo)
{
    if(printInfo == NULL)
        return;

    if (instance != NULL) {
    /***** Insert NPP_Print code here *****/
        PluginInstance* This = (PluginInstance*) instance->pdata;
        (void)This;
        PLUGIN_MSG msg;
        memset((char*)&msg, 0, sizeof(PLUGIN_MSG));
        msg.msg_id = SO_PRINT;
        msg.instance_id = (plugin_Int32)instance;
        if(!sendMsg(&msg, sizeof(PLUGIN_MSG), 1))
            debug_fprintf(NSP_LOG_APPEND, "NPP_StreamAsFile send SO_SET_WINDOW return failure \n");
       printInfo->mode = TRUE;
    /**************************************/

        if (printInfo->mode == NP_FULL) {
            /*
             * PLUGIN DEVELOPERS:
             *  If your plugin would like to take over
             *  printing completely when it is in full-screen mode,
             *  set printInfo->pluginPrinted to TRUE and print your
             *  plugin as you see fit.  If your plugin wants Netscape
             *  to handle printing in this case, set
             *  printInfo->pluginPrinted to FALSE (the default) and
             *  do nothing.  If you do want to handle printing
             *  yourself, printOne is true if the print button
             *  (as opposed to the print menu) was clicked.
             *  On the Macintosh, platformPrint is a THPrint; on
             *  Windows, platformPrint is a structure
             *  (defined in npapi.h) containing the printer name, port,
             *  etc.
             */

    /***** Insert NPP_Print code here *****\
            void* platformPrint =
                printInfo->print.fullPrint.platformPrint;
            NPBool printOne =
                printInfo->print.fullPrint.printOne;
    \**************************************/

            /* Do the default*/
            printInfo->print.fullPrint.pluginPrinted = FALSE;
        }
        else {  /* If not fullscreen, we must be embedded */
            /*
             * PLUGIN DEVELOPERS:
             *  If your plugin is embedded, or is full-screen
             *  but you returned false in pluginPrinted above, NPP_Print
             *  will be called with mode == NP_EMBED.  The NPWindow
             *  in the printInfo gives the location and dimensions of
             *  the embedded plugin on the printed page.  On the
             *  Macintosh, platformPrint is the printer port; on
             *  Windows, platformPrint is the handle to the printing
             *  device context.
             */

    /***** Insert NPP_Print code here *****\
            NPWindow* printWindow =
                &(printInfo->print.embedPrint.window);
            void* platformPrint =
                printInfo->print.embedPrint.platformPrint;
    \**************************************/
        }
    }
}

}// end of extern "C"
