Mozilla Thunderbird的扩展开发
创始人
2024-07-20 14:41:32
0

  最近遇到这样一个需求:在我们的MFC程序中控制Thunderbird。拿到这个奇怪的需求后,有了这么几个想法:1)用全局钩子试着勾住Thunderbird,可细想好像不对。2)用spy++探查点击Thunderbird的各个菜单,按钮时触发的事件,总归是win32平台上跑的程序,归根到底还是事件触发,从理论上说应该是可以在我们自己的MFC程序中模拟标目程序中的各个事件触发。

  但接触到Mozilla的socket方面的知识后,放弃了上面的想法,改用socket通信来实现此需求。一来Thunderbird源代码可以自行修改,因此可以加入一些代码使之成为一个类似HTTP服务器的服务器端程序,二来使用socket进行连接与语言无关,因此C++的程序可以和javascript的程序完成通信,但缺点也很明显,要占用端口,只涉及到本地机器的通信却使用了socket这样的网络通信机制。

  先来介绍下完成这个功能用到的基本知识:由于输入流基接口nsIInputStream没有提供任何从javascript中读取数据的方法,而只能在C++中读取,因此我们必须使用一个可脚本化的输入流来对其进行包装,这个可脚本化的输入流实现了nsIScriptableInputStream接口。

  当然也可以使用其他流类型,比如为了读取二进制数据,我们可以使用nsIBinaryInputStream,反正在javascript中,你总得以其他形式的流来包装基流。

  在Mozilla中,网络数据的读和写是在一个单独的线程中进行的,从socket中读数据是异步的,这就意味着只要socket中有数据存在,就在后台读取数据,而程序会收到数据可读的通知信息,这就意味着我们需要创建一个监听者来监听这个信息。

  对于异步读取数据,我们还需要创建一个输入流Pump,之所以Mozilla选择“泵”这个名称是因为当有数据存在时它就发送小块的数据给监听者。这个Pump实现了nsIInputStreamPump接口,这个接口继承自nsIRequest,后者提供了暂停连接和特定的缓存行为(这些一般的socket程序用不上,但像HTTP这样构建在socket上的高层次应用会用得上的)。

  pump.asyncRead(dataListener,null);

  asyncRead函数用来给Pump对象设置一个监听者,当有数据到来或连接关闭时,监听者就会被调用。监听者对象应该实现nsIStreamListener接口,这个接口有一个方法onDataAvailable,当有额外数据可读时就会被调用。此接口继承自nsIRequestObserver,这个接口有两个方法,onStartRequest,onStopRequest,这两个方法在输入流的开始和结束时分别被调用,因此这3个方法都应该实现。

  下面是客户端代码,使用最简单的阻塞式TCP连接:

  

  1. void CClientTestDlg::OnSend()  
  2.  
  3.   {//发送按钮  
  4.  
  5.   UpdateData();  
  6.  
  7.   if(str.IsEmpty())  
  8.  
  9.   {  
  10.  
  11.   list.InsertString(0,"发送的字符串不能为空。");  
  12.  
  13.   return;  
  14.  
  15.   }  
  16.  
  17.   //strcpy(msg.msg,(LPCTSTR)str);  
  18.  
  19.   char szText[1024];  
  20.  
  21.   strcpy(szText,(LPCTSTR)str);  
  22.  
  23.   SOCKET socket = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);  
  24.  
  25.   if(socket==INVALID_SOCKET)  
  26.  
  27.   {  
  28.  
  29.   list.InsertString(0,"创建socket发生错误。");  
  30.  
  31.   return;  
  32.  
  33.   }  
  34.  
  35.   SOCKADDR_IN servAddr;  
  36.  
  37.   servAddr.sin_family = AF_INET;  
  38.  
  39.   servAddr.sin_port = htons(25501);  
  40.  
  41.   servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  
  42.  
  43.   if(::connect(socket,(sockaddr*)&servAddr,sizeof(servAddr))==-1)  
  44.  
  45.   {  
  46.  
  47.   list.InsertString(0,"连接服务器发生错误。");  
  48.  
  49.   return;  
  50.  
  51.   }  
  52.  
  53.   if(::send(socket,szText,strlen(szText),0) == SOCKET_ERROR)// recv(ServerSocket,buf,sizeof(buf),0);  
  54.  
  55.   {  
  56.  
  57.   list.InsertString(0,"发送数据发生错误。");  
  58.  
  59.   return;  
  60.  
  61.   }  
  62.  
  63.   int bytesRecv = SOCKET_ERROR;  
  64.  
  65.   char buffer[1024];  
  66.  
  67.   memset(buffer,0,1024);  
  68.  
  69.   bytesRecv = recv( socket, buffer, 1024, 0 );  
  70.  
  71.   DWORD dwerr = WSAGetLastError();  
  72.  
  73.   CString str;  
  74.  
  75.   str.Format("%s",buffer);  
  76.  
  77.   list.InsertString(0,str);  
  78.  
  79.   ::closesocket(socket);  
  80.  
  81.   } 

#p#

  服务器端代码,通过修改Thunderbird源代码,使之成为一个类似HTTP服务器的服务器端

 

  1.  //***************************************************************************  
  2.  
  3.   //Author: phinecos  
  4.  
  5.   //Date : 2008/5/19  
  6.  
  7.   //Description:服务器监听对象  
  8.  
  9.   //Contact:phinecos@163.com  
  10.  
  11.   //***************************************************************************  
  12.  
  13.   //服务器端对象  
  14.  
  15.   const CI = Components.interfaces, CC = Components.classes, CR = Components.results;  
  16.  
  17.   var gServer = null;//服务器对象  
  18.  
  19.   var gUtility = null;//工具类对象  
  20.  
  21.   //var gConnection = null;//客户端连接对象  
  22.  
  23.   function tBirdBiffServerOnLoad()  
  24.  
  25.   {//启动监听服务器  
  26.  
  27.   try  
  28.  
  29.   {  
  30.  
  31.   gUtility = new tBirdBiffUtility();//新建日志对象  
  32.  
  33.   gUtility.initialize();  
  34.  
  35.   gUtility.log("tBirdBiffServerUi.tBirdBiffServerOnLoad", "Thunderbird biff server loaded");  
  36.  
  37.   gServer = new tBirdBiffServer();//新建服务器对象  
  38.  
  39.   gServer.initialize();//初始化服务器端  
  40.  
  41.   gUtility.log("tBirdBiffServerOnLoad","start server");  
  42.  
  43.   }  
  44.  
  45.   catch(err)  
  46.  
  47.   {  
  48.  
  49.   gUtility.log("tBirdBiffServerOnLoad","start server failed");  
  50.  
  51.   }  
  52.  
  53.   }  
  54.  
  55.   function tBirdBiffServerOnClose()  
  56.  
  57.   {//关闭服务器  
  58.  
  59.   gUtility.log("tBirdBiffServerOnClose","close server");  
  60.  
  61.   gServer.finalize();  
  62.  
  63.   gServer = null;  
  64.  
  65.   gUtility = null;  
  66.  
  67.   }  
  68.  
  69.   function tBirdBiffServer()  
  70.  
  71.   {  
  72.  
  73.   this.serverSocket = null;//服务器端socket  
  74.  
  75.   this.port = null;//服务器端口  
  76.  
  77.   this.initialized = false;  
  78.  
  79.   gUtility.log("tBirdBiffServer constructor","construct ok");  
  80.  
  81.   }  
  82.  
  83.   tBirdBiffServer.prototype =  
  84.  
  85.   {  
  86.  
  87.   getServerSocket: function()  
  88.  
  89.   {//创建服务器端socket  
  90.  
  91.   this.serverSocket = CC["@mozilla.org/network/server-socket;1"].createInstance(CI.nsIServerSocket);  
  92.  
  93.   if(!this.serverSocket)  
  94.  
  95.   {  
  96.  
  97.   gUtility.log("tBirdBiffServer.getServerSocket","Unable to get a server socket");  
  98.  
  99.   }  
  100.  
  101.   else  
  102.  
  103.   {  
  104.  
  105.   try  
  106.  
  107.   {  
  108.  
  109.   this.serverSocket.init(this.port, false, -1);//初始化socket,绑定到端口port上  
  110.  
  111.   this.serverSocket.asyncListen(tBirdBiffServerSocketListener);//开始异步监听  
  112.  
  113.   gUtility.log("tBirdBiffServer.getServerSocket","Server socket established");  
  114.  
  115.   }  
  116.  
  117.   catch(e)  
  118.  
  119.   {  
  120.  
  121.   gUtility.log("tBirdBiffServer.getServerSocket, Server socket established error");  
  122.  
  123.   this.serverSocket = null;  
  124.  
  125.   }  
  126.  
  127.   }  
  128.  
  129.   },  
  130.  
  131.   closeServerSocket: function()  
  132.  
  133.   {//关闭服务器端socket  
  134.  
  135.   if(!this.serverSocket)  
  136.  
  137.   {  
  138.  
  139.   this.serverSocket.close(null);  
  140.  
  141.   this.serverSocket = null;  
  142.  
  143.   }  
  144.  
  145.   },  
  146.  
  147.   initialize: function()  
  148.  
  149.   {  
  150.  
  151.   if(this.initialized)  
  152.  
  153.   {  
  154.  
  155.   return;  
  156.  
  157.   }  
  158.  
  159.   this.port = 25501;//获取端口号  
  160.  
  161.   gUtility.log("tBirdBiffServer.initialize","port is: "+this.port);  
  162.  
  163.   this.getServerSocket();//创建服务器端socket  
  164.  
  165.   this.initialized = true;  
  166.  
  167.   gUtility.log("tBirdBiffServer.initialize","initialize ok!");  
  168.  
  169.   },  
  170.  
  171.   finalize: function()  
  172.  
  173.   {//退出程序  
  174.  
  175.   if(!this.initialized)  
  176.  
  177.   {  
  178.  
  179.   return;  
  180.  
  181.   }  
  182.  
  183.   this.closeServerSocket();//关闭服务器端socket  
  184.  
  185.   gUtility.log("tBirdBiffServer.finalize","server Finalized");  
  186.  
  187.   },  
  188.  
  189.   }  
  190.  
  191.   var listener =  
  192.  
  193.   {  
  194.  
  195.   finished : function(data)  
  196.  
  197.   {//数据读取完毕  
  198.  
  199.   ParseCommand(data);//解析命令字  
  200.  
  201.   }  
  202.  
  203.   }  
  204.  
  205.   const tBirdBiffServerSocketListener =  
  206.  
  207.   {  
  208.  
  209.   onSocketAccepted: function(serverSocket, clientSocket)  
  210.  
  211.   {//接受来自客户端的socket请求  
  212.  
  213.   gUtility.log("tBirdBiffServerSocketListener.onSocketAccepted","Got a connection from: " + clientSocket.host + ":" + clientSocket.port);  
  214.  
  215.   var outputData = "Ok,server get your command";  
  216.  
  217.   var outstream = clientSocket.openOutputStream(0,0,0);  
  218.  
  219.   outstream.write(outputData,outputData.length);  
  220.  
  221.   try  
  222.  
  223.   {  
  224.  
  225.   //打开输入流  
  226.  
  227.   var stream = clientSocket.openInputStream(0,0,0);  
  228.  
  229.   var instream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);  
  230.  
  231.   instream.init(stream);  
  232.  
  233.   var dataListener =  
  234.  
  235.   {  
  236.  
  237.   data : "",//来自客户端的数据  
  238.  
  239.   onStartRequest: function(request, context){},  
  240.  
  241.   onStopRequest: function(request, context, status){  
  242.  
  243.   instream.close();  
  244.  
  245.   outstream.close();  
  246.  
  247.   listener.finished(this.data);  
  248.  
  249.   },  
  250.  
  251.   onDataAvailable: function(request, context, inputStream, offset, count){  
  252.  
  253.   this.data += instream.read(count);  
  254.  
  255.   },  
  256.  
  257.   };  
  258.  
  259.   var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance(Components.interfaces.nsIInputStreamPump);  
  260.  
  261.   pump.init(stream, -1, -1, 0, 0, false);  
  262.  
  263.   pump.asyncRead(dataListener,null);  
  264.  
  265.   }  
  266.  
  267.   catch(err)  
  268.  
  269.   {  
  270.  
  271.   gUtility.log("tBirdBiffServerConnection.setSocket",e);  
  272.  
  273.   return false;  
  274.  
  275.   }  
  276.  
  277.   },  
  278.  
  279.   onStopListening: function(serverSocket, status)  
  280.  
  281.   {//服务器端停止监听  
  282.  
  283.   gUtility.log("tBirdBiffServerSocketListener.onStopListening","Server socket has stopped listening");  
  284.  
  285.   }  
  286.  
  287.   } 

【编辑推荐】

  1. Thunderbird 邮件导入导出的方法
  2. Thunderbird的安装方法
  3. Thunderbird 配置手册
  4. Ubuntu下安装Thunderbird
  5. Thunderbird 的相关扩展
  6. Thunderbird中常见问题解决
  7. 图文并茂 Thunderbird设置教程
  8. Thunderbird的使用技巧

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
2012年第四季度互联网状况报... [[71653]]  北京时间4月25日消息,据国外媒体报道,全球知名的云平台公司Akamai Te...