source: BMXNET_RPMS_dotNET_UTILITIES-BMX/branch/BMX41000/IHS BMX Framework/IndianHealthService.BMXNet/BMXNetNativeLib.cs@ 1806

Last change on this file since 1806 was 1146, checked in by Sam Habiel, 13 years ago

Initial Import of BMX4

File size: 20.5 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Text;
4using System.IO;
5using System.Net.Sockets;
6using System.Net;
7using System.Security.Cryptography;
8using System.Security.Permissions;
9using System.Security.Principal;
10using System.Threading;
11using System.Timers;
12
13namespace IndianHealthService.BMXNet
14{
15 /// <summary>
16 /// BMXNetLib implements low-level socket connectivity to RPMS databases.
17 /// The VA RPC Broker must be running on the RPMS server in order for
18 /// BMXNetLib to connect.
19 /// </summary>
20 [DnsPermission(SecurityAction.Assert, Unrestricted = true)]
21 public class BMXNetNativeLib:BMXNetLib
22 {
23 public BMXNetNativeLib()
24 {
25 m_sWKID = "BMX";
26 m_sWINH = "";
27 m_sPRCH = "";
28 m_sWISH = "";
29 m_cHDR = ADEBHDR(m_sWKID,m_sWINH,m_sPRCH,m_sWISH);
30
31 }
32
33
34 #region Piece Functions
35
36 /// <summary>
37 /// Corresponds to M's $L(STRING,DELIMITER)
38 /// </summary>
39 /// <param name="sInput"></param>
40 /// <param name="sDelim"></param>
41 /// <returns></returns>
42 public static int PieceLength(string sInput, string sDelim)
43 {
44 char[] cDelim = sDelim.ToCharArray();
45 string [] cSplit = sInput.Split(cDelim);
46 return cSplit.GetLength(0);
47 }
48
49 /// <summary>
50 /// Corresponds to M's $$Piece function
51 /// </summary>
52 /// <param name="sInput"></param>
53 /// <param name="sDelim"></param>
54 /// <param name="nNumber"></param>
55 /// <returns></returns>
56 public static string Piece(string sInput, string sDelim, int nNumber)
57 {
58 try
59 {
60 char[] cDelim = sDelim.ToCharArray();
61 string [] cSplit = sInput.Split(cDelim);
62 int nLength = cSplit.GetLength(0);
63 if ((nLength < nNumber)||(nNumber < 1))
64 return "";
65 return cSplit[nNumber-1];
66 }
67 catch (Exception bmxEx)
68 {
69 string sMessage = bmxEx.Message + bmxEx.StackTrace;
70 throw new BMXNetException(sMessage);
71 }
72
73 }
74
75 public static string Piece(string sInput, string sDelim, int nNumber, int nEnd)
76 {
77 try
78 {
79 if (nNumber < 0)
80 nNumber = 1;
81
82 if (nEnd < nNumber)
83 return "";
84
85 if (nEnd == nNumber)
86 return Piece(sInput, sDelim, nNumber);
87
88 char[] cDelim = sDelim.ToCharArray();
89 string [] cSplit = sInput.Split(cDelim);
90 int nLength = cSplit.GetLength(0);
91 if ((nLength < nNumber)||(nNumber < 1))
92 return "";
93
94 //nNumber = 1-based index of the starting element to return
95 //nLength = count of elements
96 //nEnd = 1-based index of last element to return
97 //nCount = number of elements past nNumber to return
98
99 //convert nNumber to 0-based index:
100 nNumber--;
101
102 //convert nEnd to 0-based index;
103 nEnd--;
104
105 //Calculate nCount;
106 int nCount = nEnd - nNumber + 1;
107
108 //Adjust nCount for number of elements
109 if (nCount + nNumber >= nLength)
110 {
111 nCount = nLength - nNumber;
112 }
113
114 string sRet = string.Join(sDelim, cSplit, nNumber, nCount );
115 return sRet;
116 }
117 catch (Exception bmxEx)
118 {
119 string sMessage = bmxEx.Message + bmxEx.StackTrace;
120 throw new BMXNetException(sMessage);
121 }
122 }
123
124 #endregion Piece Functions
125
126 #region RPX Fields
127
128 private string m_sWKID;
129 private string m_sWISH;
130 private string m_sPRCH;
131 private string m_sWINH;
132 private string m_cHDR;
133 private string m_cAppContext;
134 private bool m_bConnected;
135 private int m_nMServerPort;
136 private string m_cServerAddress;
137 private string m_cDUZ;
138 private string m_cLoginFacility;
139 private TcpClient m_pCommSocket;
140 private string m_sNameSpace = "";
141 private int m_nReceiveTimeout = 30000;
142
143 #endregion RPX Fields
144
145
146
147 /// <summary>
148 /// Returns index of first instance of sSubString in sString.
149 /// If sSubString not found, returns -1.
150 /// </summary>
151 /// <param name="sString"></param>
152 /// <param name="sSubString"></param>
153 /// <returns></returns>
154
155
156 protected virtual void OpenConnectionCommon(string sServerAddress)
157 {
158 try
159 {
160 m_cServerAddress = sServerAddress;
161
162 //Connect with the server
163 TcpClient connector = new TcpClient();
164
165 try
166 {
167 connector = new TcpClient();
168 connector.Connect(m_cServerAddress, m_nMServerPort);
169 }
170 catch (SocketException exSocket)
171 {
172 string s = exSocket.Message + exSocket.StackTrace;
173 throw new BMXNetException(s);
174 }
175
176 //Prepare & send the connect message
177 string cSend = "TCPconnect^" + m_sNameSpace + "^^";
178 int nLen = cSend.Length;
179 string sLen = nLen.ToString();
180 sLen = sLen.PadLeft(5, '0');
181 cSend = "{BMX}" + sLen + cSend;
182
183 NetworkStream ns = connector.GetStream();
184 byte[] sendBytes = Encoding.ASCII.GetBytes(cSend);
185 ns.Write(sendBytes,0,sendBytes.Length);
186
187 m_pCommSocket = connector;
188 return;
189
190 }
191 catch (BMXNetException bmxEx)
192 {
193 throw bmxEx;
194 }
195 catch (Exception ex)
196 {
197 string s = ex.Message + ex.StackTrace;
198 throw new BMXNetException(s);
199 }
200 }//End OpenConnectionCommon
201
202 [SocketPermissionAttribute(SecurityAction.Assert,
203 Access="Connect",
204 Host="All",
205 Port="All",
206 Transport="All")]
207 public virtual bool OpenConnection(string sServerAddress, WindowsIdentity winIdentity)
208 {
209 try
210 {
211 OpenConnectionCommon(sServerAddress);
212 bool bSecurity;
213 try
214 {
215 bSecurity = SendSecurityRequest(winIdentity);
216
217 }
218 catch (Exception ex)
219 {
220 //Close the connection
221 SendString(m_pCommSocket, "#BYE#");
222 m_pCommSocket.Close();
223 m_bConnected = false;
224 m_cServerAddress = "";
225 throw ex;
226 }
227
228 m_bConnected = bSecurity;
229 return m_bConnected;
230 }
231 catch (BMXNetException bmxEx)
232 {
233 throw bmxEx;
234 }
235 catch (Exception ex)
236 {
237 string s = ex.Message + ex.StackTrace;
238 throw new BMXNetException(s);
239 }
240 }
241
242 StreamWriter m_LogWriter;
243 bool m_bLogging = false;
244
245 public void StartLog()
246 {
247 try
248 {
249 if (m_bLogging)
250 {
251 throw new Exception("Already logging.");
252 }
253 string sFile = "BMXLog " + DateTime.Now.DayOfYear.ToString() + " " +
254 DateTime.Now.Hour.ToString() + " " + DateTime.Now.Minute.ToString()
255 + " " + DateTime.Now.Second.ToString() + ".log";
256 StartLog(sFile);
257 return;
258 }
259 catch (Exception ex)
260 {
261 throw ex;
262 }
263 }
264
265 public void StartLog(string LogFileName)
266 {
267 try
268 {
269 if (m_bLogging)
270 {
271 throw new Exception("Already logging.");
272 }
273 m_LogWriter = File.AppendText(LogFileName);
274 m_bLogging = true;
275 return;
276 }
277 catch (Exception ex)
278 {
279 throw ex;
280 }
281 }
282
283 public void StopLog()
284 {
285 try
286 {
287 //Close the writer and underlying file.
288 if (m_bLogging == false)
289 {
290 return;
291 }
292 m_LogWriter.Close();
293 m_bLogging = false;
294 return;
295 }
296 catch (Exception ex)
297 {
298 throw ex;
299 }
300 }
301
302 private static void Log(String logMessage, TextWriter w)
303 {
304 w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
305 DateTime.Now.ToLongDateString());
306 w.WriteLine(" :");
307 w.WriteLine(" :{0}", logMessage);
308 w.WriteLine("-------------------------------");
309 // Update the underlying file.
310 w.Flush();
311 }
312
313 [SocketPermissionAttribute(SecurityAction.Assert,
314 Access="Connect",
315 Host="All",
316 Port="All",
317 Transport="All")]
318 public bool OpenConnection(string sServerAddress, string sAccess, string sVerify)
319 {
320 try
321 {
322 this.OpenConnectionCommon(sServerAddress);
323
324 try
325 {
326 bool bSecurity = SendSecurityRequest(sAccess, sVerify);
327 }
328 catch (Exception ex)
329 {
330 //Close the connection
331 SendString(m_pCommSocket, "#BYE#");
332 m_pCommSocket.Close();
333 m_bConnected = false;
334 m_cServerAddress = "";
335 throw ex;
336 }
337
338 m_bConnected = true;
339 return m_bConnected;
340 }
341 catch (BMXNetException bmxEx)
342 {
343 throw bmxEx;
344 }
345 catch (Exception ex)
346 {
347 string s = ex.Message + ex.StackTrace;
348 throw new BMXNetException(s);
349 }
350 }
351
352 public override bool IsConnected
353 {
354 get
355 {
356 return this.m_bConnected;
357 }
358 }
359
360
361 protected override String SendReceiveString(string cSendString, string cMult)
362 {
363 this.SendString(this.m_pCommSocket, cSendString, cMult);
364 return this.ReceiveString(this.m_pCommSocket);
365 }
366
367
368 protected virtual void SendString(TcpClient tcpClient, string cSendString)
369 {
370 string sMult = "";
371 SendString(tcpClient, cSendString, sMult);
372 }
373
374 protected virtual void SendString(TcpClient tcpClient, string cSendString, string cMult)
375 {
376 String encodedString = this.EncodeSendString(cSendString, cMult);
377
378 NetworkStream ns = tcpClient.GetStream();
379 byte[] sendBytes = Encoding.ASCII.GetBytes(encodedString);
380 ns.Write(sendBytes,0,sendBytes.Length);
381 if (this.m_bLogging == true)
382 {
383 Log("Sent: " + cSendString, this.m_LogWriter);
384 }
385 return;
386 }
387
388
389 private string ReceiveString(TcpClient tcpClient)
390 {
391 NetworkStream ns = tcpClient.GetStream();
392
393 int nTimeOut = this.m_nReceiveTimeout;
394 int nCnt = 0;
395 int nTimeElapsed = 0;
396 while (ns.DataAvailable == false)
397 {
398 if (nCnt > 9999)
399 break;
400 if (nTimeElapsed > nTimeOut)
401 break;
402 nCnt++;
403 nTimeElapsed += 50;
404 Thread.Sleep(50);
405 }
406
407 Debug.Assert(ns.DataAvailable == true);
408 if (ns.DataAvailable == false)
409 {
410 this.CloseConnection();
411 throw new Exception("BMXNetLib.ReceiveString timeout. Connection Closed.");
412 //return "";
413 }
414
415 byte[] bReadBuffer = new byte[1024];
416 string sReadBuffer = "";
417 StringBuilder sbAll = new StringBuilder("", 1024);
418 int numberOfBytesRead = 0;
419
420 // Incoming message may be larger than the buffer size.
421
422 bool bFinished = false;
423 int nFind = -1;
424 bool bStarted = false;
425 int lpBuf = 0;
426 string sError = "";
427 string sAppError = "";
428 do
429 {
430
431 numberOfBytesRead = ns.Read(bReadBuffer, 0, bReadBuffer.Length);
432 if ((numberOfBytesRead == 1)&&(bStarted == false))
433 {
434 Thread.Sleep(15);
435 numberOfBytesRead += ns.Read(bReadBuffer,1, bReadBuffer.Length-1);
436 //Debug.Write("ReceiveString waiting for data...\n");
437 }
438 if (bStarted == false)
439 {
440 //Process error info at beginning of returned string
441 int nErrLen = bReadBuffer[0];
442 int nAppLen = bReadBuffer[bReadBuffer[0]+1];
443 if ((bReadBuffer[2] == 0)&&(bReadBuffer[3] == 0))
444 { //special case: M error trap invoked in SND^XWBTCPC
445 lpBuf += 2;
446 }
447 sError = Encoding.ASCII.GetString(bReadBuffer, lpBuf + 1, nErrLen);
448 if (sError != "")
449 {
450 throw new BMXNetException(sError);
451 }
452 sAppError = Encoding.ASCII.GetString(bReadBuffer, lpBuf+1+nErrLen+1, nAppLen);
453 lpBuf += (nErrLen + nAppLen + 2);
454 numberOfBytesRead -= (nErrLen + nAppLen + 2);
455 bStarted = true;
456 }
457
458 nFind = FindChar(bReadBuffer, (char) 4);
459 if (nFind > -1)
460 bFinished = true;
461 Debug.Assert(numberOfBytesRead > -1);
462 sReadBuffer = Encoding.ASCII.GetString(bReadBuffer, lpBuf, numberOfBytesRead);
463 lpBuf = 0;
464 if (nFind > -1)
465 {
466 sbAll.Append(sReadBuffer, 0, numberOfBytesRead -1);
467 }
468 else
469 {
470 sbAll.Append(sReadBuffer);
471 }
472 }
473 while(!bFinished);
474
475 String decodedReceiveString = this.DecodeReceiveString(sbAll.ToString());
476
477 if (this.m_bLogging)
478 {
479 Log("Received: " + decodedReceiveString, this.m_LogWriter);
480 }
481 return decodedReceiveString;
482
483 }
484 private bool SendSecurityRequest(WindowsIdentity winIdentity)
485 {
486 string strReceive = "";
487 string cMSG;
488 string sTest;
489
490 //Build AV Call
491 cMSG = ADEBLDMsg(m_cHDR, "BMX AV CODE", winIdentity.Name);
492 SendString(m_pCommSocket, cMSG);
493
494 strReceive = ReceiveString(m_pCommSocket);
495 sTest = strReceive.Substring(0,3);
496
497
498 char[] cDelim = {(char) 13,(char) 10,(char) 0};
499 string sDelim = new string(cDelim);
500 int nPiece = 1;
501 m_cDUZ = Piece(strReceive, sDelim , nPiece);
502 if ((m_cDUZ == "0")||(m_cDUZ == ""))
503 {
504 nPiece = 7;
505 string sReason = Piece(strReceive, sDelim, nPiece);
506 throw new Exception(sReason);
507 }
508
509 return true;
510 }
511
512 private bool SendSecurityRequest(string sAccess, string sVerify)
513 {
514 string strReceive = "";
515 string cMSG;
516 sAccess = sAccess.ToUpper();
517 sVerify = sVerify.ToUpper();
518
519 //Build AV Call
520 string strAV = sAccess + ";" + sVerify;
521 strAV = ADEEncryp(strAV);
522 cMSG = ADEBLDMsg(m_cHDR, "XUS AV CODE", strAV);
523 SendString(m_pCommSocket, cMSG);
524
525 strReceive = ReceiveString(m_pCommSocket);
526
527 char[] cDelim = {(char) 13,(char) 10,(char) 0};
528 string sDelim = new string(cDelim);
529 int nPiece = 1;
530 this.DUZ = Piece(strReceive, sDelim , nPiece);
531
532 this.UserName = sAccess;
533
534 if ((this.DUZ == "0") || (this.DUZ == ""))
535 {
536 nPiece = 7;
537 string sReason = Piece(strReceive, sDelim, nPiece);
538 throw new Exception(sReason);
539 }
540
541 return true;
542 }
543
544 public override void CloseConnection()
545 {
546 if (!m_bConnected)
547 {
548 return;
549 }
550 SendString(m_pCommSocket, "#BYE#");
551 m_pCommSocket.Close();
552 m_bConnected = false;
553 m_cServerAddress = "";
554 // m_cDUZ2 = "";
555 this.DUZ = "";
556 }
557
558 public bool Lock(string Variable)
559 {
560 return Lock(Variable, "", "");
561 }
562
563 public bool Lock(string Variable, string Increment)
564 {
565 return Lock(Variable, Increment, "");
566 }
567
568 /// <summary>
569 /// Lock a local or global M variable
570 /// Returns true if lock is obtained during TimeOut seconds
571 /// Use + to increment, - to decrement lock.
572 /// </summary>
573 /// <param name="Variable"></param>
574 /// <param name="Increment"></param>
575 /// <param name="TimeOut"></param>
576 /// <returns></returns>
577 public bool Lock(string Variable, string Increment, string TimeOut)
578 {
579 try
580 {
581 string sContext = this.AppContext;
582 this.AppContext = "BMXRPC";
583 Variable = Variable.Replace("^","~");
584 string sRet = "0";
585 bool bRet = false;
586 string sParam = Variable + "^" + Increment + "^" + TimeOut;
587 sRet = TransmitRPC("BMX LOCK", sParam);
588 bRet = (sRet == "1")?true:false;
589 this.AppContext = sContext;
590 return bRet;
591 }
592 catch (Exception ex)
593 {
594 string sMsg = ex.Message;
595 return false;
596 }
597 }
598
599 static ReaderWriterLock m_rwl = new ReaderWriterLock();
600 private int m_nRWLTimeout = 30000; //30-second default timeout
601
602 /// <summary>
603 /// Returns a reference to the internal ReaderWriterLock member.
604 /// </summary>
605 public ReaderWriterLock BMXRWL
606 {
607 get
608 {
609 return m_rwl;
610 }
611 }
612
613 /// <summary>
614 /// Sets and returns the timeout in milliseconds for locking the transmit port.
615 /// If the transmit port is unavailable an ApplicationException will be thrown.
616 /// </summary>
617 public int RWLTimeout
618 {
619 get
620 {
621 return m_nRWLTimeout;
622 }
623 set
624 {
625 m_nRWLTimeout = value;
626 }
627 }
628
629
630
631 public virtual string TransmitRPC(string sRPC, string sParam, int nLockTimeOut)
632 {
633 try
634 {
635 try
636 {
637 if (m_bConnected == false)
638 {
639 throw new BMXNetException("BMXNetLib.TransmitRPC failed because BMXNetLib is not connected to RPMS.");
640 }
641 Debug.Assert(m_cDUZ != "");
642 Debug.Assert(m_pCommSocket != null);
643
644 string sOldAppContext = "";
645 if (sRPC.StartsWith("BMX")&&(this.m_cAppContext != "BMXRPC"))
646 {
647 sOldAppContext = this.m_cAppContext;
648 this.AppContext = "BMXRPC";
649 }
650 string sMult = "";
651 string sSend = ADEBLDMsg(m_cHDR, sRPC, sParam, ref sMult);
652 string strResult = SendReceiveString(sSend,sMult);
653 //Debug.Write("TransmitRPC Received: " + strResult + "\n");
654
655 if (sOldAppContext != "")
656 {
657 this.AppContext = sOldAppContext;
658 }
659 return strResult;
660 }
661 catch (Exception ex)
662 {
663 if (ex.Message == "Unable to write data to the transport connection.")
664 {
665 m_bConnected = false;
666 }
667 throw ex;
668 }
669 finally
670 {
671 }
672 }
673 catch (ApplicationException aex)
674 {
675 // The writer lock request timed out.
676 Debug.Write("TransmitRPC writer lock request timed out.\n");
677 throw aex;
678 }
679 catch (Exception OuterEx)
680 {
681 throw OuterEx;
682 }
683 }
684
685 public string TransmitRPC(string sRPC, string sParam)
686 {
687 try
688 {
689 return TransmitRPC(sRPC, sParam, m_nRWLTimeout);
690 }
691 catch (ApplicationException aex)
692 {
693 throw aex;
694 }
695 catch (Exception ex)
696 {
697 throw ex;
698 }
699 }
700
701 public override string GetLoginFacility()
702 {
703 try
704 {
705 if (m_bConnected == false)
706 {
707 throw new BMXNetException("BMXNetLib is not connected to RPMS");
708 }
709
710 if (m_cLoginFacility != "")
711 {
712 return m_cLoginFacility;
713 }
714
715 Debug.Assert(m_pCommSocket != null);
716 Debug.Assert(m_cDUZ != "");
717 SendString(m_pCommSocket, ADEBLDMsg(m_cHDR, "BMXGetFac", m_cDUZ));
718 string sFac = ReceiveString(m_pCommSocket);
719 m_cLoginFacility = sFac;
720 return sFac;
721 }
722 catch (BMXNetException bmxEx)
723 {
724 string sMessage = bmxEx.Message + bmxEx.StackTrace;
725 throw new BMXNetException(sMessage);
726 }
727 catch (Exception ex)
728 {
729 throw ex;
730 }
731 }
732
733
734
735 #region RPX Properties
736
737 /// <summary>
738 /// Set and retrieve the timeout, in milliseconds, to receive a response from the RPMS server.
739 /// If the retrieve time exceeds the timeout, an exception will be thrown and the connection will be closed.
740 /// The default is 30 seconds.
741 /// </summary>
742 public int ReceiveTimeout
743 {
744 get { return m_nReceiveTimeout; }
745 set { m_nReceiveTimeout = value; }
746 }
747
748 public string WKID
749 {
750 get
751 {
752 return m_sWKID;
753 }
754 set
755 {
756 m_sWKID = value;
757 }
758 }
759
760 public string PRCH
761 {
762 get
763 {
764 return m_sPRCH;
765 }
766 set
767 {
768 m_sPRCH = value;
769 }
770 }
771
772 public string WINH
773 {
774 get
775 {
776 return m_sWINH;
777 }
778 set
779 {
780 m_sWINH = value;
781 }
782 }
783
784 public string WISH
785 {
786 get
787 {
788 return m_sWISH;
789 }
790 set
791 {
792 m_sWISH = value;
793 }
794 }
795
796 /// <summary>
797 /// Gets/sets the Kernel Application context
798 /// Throws an exception if unable to set the context.
799 /// </summary>
800 public string AppContext
801 {
802 get
803 {
804 return m_cAppContext;
805 }
806 set
807 {
808 //Send the changed context to RPMS
809 if ((m_bConnected == true) && (value != ""))
810 {
811 try
812 {
813 string sRPC = ADEEncryp(value);
814 string sAuthentication = TransmitRPC("XWB CREATE CONTEXT", sRPC);
815
816 if (BMXNetLib.FindSubString(sAuthentication, "does not have access to option") > -1)
817 {
818 throw new BMXNetException(sAuthentication);
819 }
820
821 }
822 catch (Exception ex)
823 {
824 Debug.Write(ex.Message);
825 throw ex;
826 }
827 }
828 m_cAppContext = value;
829 }
830 }
831
832 public bool Connected
833 {
834 get
835 {
836 return m_bConnected;
837 }
838 }
839
840
841 public string MServerAddress
842 {
843 get
844 {
845 return m_cServerAddress;
846 }
847 }
848
849 public string MServerNamespace
850 {
851 get
852 {
853 return m_sNameSpace;
854 }
855 set
856 {
857 m_sNameSpace = value;
858 }
859 }
860
861 public int MServerPort
862 {
863 get
864 {
865 return m_nMServerPort;
866 }
867 set
868 {
869 m_nMServerPort = value;
870 }
871 }
872
873 public string NameSpace
874 {
875 get
876 {
877 return m_sNameSpace;
878 }
879 set
880 {
881 m_sNameSpace = value;
882 }
883 }
884
885 #endregion RPX Properties
886
887 }
888}
Note: See TracBrowser for help on using the repository browser.