Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / PHP

A Solution to the Web Security Restrictions on the Client Machine

4.71/5 (5 votes)
19 Aug 2016CPOL2 min read 9.5K   119  
PHP and C# working together via thrift framework

Introduction

This example allows you to execute commands, openfiles, etc. on a client machine on an intranet web environment.

 

Background

Some time ago, I was developing an application to control the development of construction projects, so my client wanted to be a web application and that was mandatory. One of the challenges was that within this web application should open drawings and documents by clicking on a link in a web page. As we all know for security reasons this is not possible, so that was when I decided to use a desktop support software to execute these tasks on the client machine. And the glue between the intranet web application and the desktop software was made by THRIFT (an interface definition language and binary communication protocol).

Using the Application

 

Note: You must run composer to get vendors on symfony ....

I made two sample projects one is a symfony 2.8 web application that acts as a client in the world of thrift, and the other is a C# application that is the server and must run on client machine. All start writing a .thrift file that is an interface definition made up of thrift types and services like this:

C++
namespace csharp ThriftServer
namespace php ThriftSampleBundle.Util
struct FileInf
{
  1: string FileName,
  2: string extension,
  3: bool isDir
}
service OpenFileService{
    bool openFile (1:FileInf info,2:string parameters),
    list <FileInf> listFolder(1:string path),
}

Here are two definitions of methods, one to list the folders and files in a specified path and another to run or open a specific file, all that on the client machine. Then run the thrift command to generate the client and server codes.

The general syntax is:

thrift --gen <language> <Thrift filename

For PHP:

thrift --gen php OpenFileService.thrift

For C#:

thrift --gen csharp OpenFileService.thrift

So you must implement those two methods on server and call via RPC from client.

Some codes from PHP acting as client, please note as I use "$this->request->getClientIp()" to get the intranet client IP and know where to connect. So if the web browser has a proxy conf, you must specify that you don't use proxy for this site because otherwise it will get a wrong IP address.

PHP
private function openConnection() {
        
            // Create a thrift connection (Boiler plate)
            $socket = new TSocket($this->request->getClientIp(), '9090');

            $this->transport = new TBufferedTransport($socket);
            $protocol = new TBinaryProtocol($this->transport);

            // Create a calculator client
            $this->client = new OpenFileServiceClient($protocol);

            // Open up the connection
            $this->transport->open();        
    }

To call listFolder($path) method on C# server from PHP client:

PHP
public function listFolder($path) {
        try {
            $this->openConnection();
            // Perform operation on the server
            $result=$this->client->listFolder($path);
           
            // And finally, we close the thrift connection
            $this->transport->close();
            return $result;
        } 

        catch (\Exception $tx) {
            // a general thrift exception, like no such server
            echo "ThriftException: ".$tx->getMessage()."\r\n";
        }
    }

To call openFile($path,$extension) method on C# server form PHP client:

PHP
public function openFile($path,$extension) {
        try {
            $this->openConnection();


            $fileInfo=new FileInf();
                $fileInfo->FileName=$path;
                $fileInfo->extension=$extension;
            // Perform operation on the server
            $this->client->openFile($fileInfo,null);

            // And finally, we close the thrift connection
            $this->transport->close();
        } 

        catch (\Exception $tx) {
            // a general thrift exception, like no such server
            echo "ThriftException: ".$tx->getMessage()."\r\n";
        }
    }

Now let's see how to start the server side in C#:

C#
private void thrift(){
            //Thrift stuff
            var handler = new OpenFileHandler();
            var processor = new OpenFileService.Processor(handler);
            
            TServerTransport transport = new TServerSocket(9090);
            TServer server = new TThreadPoolServer(processor, transport);
            
            server.Serve();
            //end thrift
        }

And the service handler implementation:

C#
/*
 * Created by SharpDevelop.
 * User: lca85
 * Date: 8/16/2016
 * Time: 1:09 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace ThriftServer
{
    /// <summary>
    /// Description of OpenFileHandler.
    /// </summary>
    public class OpenFileHandler:OpenFileService.Iface
    {
        public FileInf fileToOpen;
        public OpenFileHandler()
        {
        }               
        
        public bool openFile(FileInf info, string parameters)
        {            
            System.Diagnostics.Process.Start(info.FileName);
            return true;
        }
        
        public IAsyncResult Begin_openFile(AsyncCallback callback, 
                                           object state, FileInf info, string parameters)
        {
            throw new NotImplementedException();
        }
        
        public bool End_openFile(IAsyncResult asyncResult)
        {
            throw new NotImplementedException();
        }
        
        public List<FileInf> listFolder(string path)
        {
            DirectoryInfo di=new DirectoryInfo(path);
            FileInfo[] files=di.GetFiles();
            DirectoryInfo[] directories=di.GetDirectories();
            List<FileInf>aux_list=new List<FileInf>();
            foreach (DirectoryInfo element in directories) {
                FileInf aux_file=new FileInf();
                aux_file.FileName=element.FullName;
                aux_file.Extension="";
                aux_file.IsDir=true;
                aux_list.Add(aux_file);
            }
            foreach (FileInfo element in files) {
                FileInf aux_file=new FileInf();
                aux_file.FileName=element.FullName;
                aux_file.Extension=element.Extension.Replace(".","");
                aux_file.IsDir=false;
                aux_list.Add(aux_file);
            }
            return aux_list;
        }
        
        public IAsyncResult Begin_listFolder(AsyncCallback callback, object state, string path)
        {
            throw new NotImplementedException();
        }
        
        public List<FileInf> End_listFolder(IAsyncResult asyncResult)
        {
            throw new NotImplementedException();
        }
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)