1
// Copyright 2006 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
6
using System.Collections.Generic;
8
using System.Threading;
9
using System.Reflection;
16
public partial class Connection
18
//TODO: reconsider this field
22
internal Transport Transport {
30
protected Connection () {}
32
internal Connection (Transport transport)
34
this.transport = transport;
35
transport.Connection = this;
37
//TODO: clean this bit up
38
ns = transport.Stream;
41
//should this be public?
42
internal Connection (string address)
44
OpenPrivate (address);
49
bool isConnected = false;
50
public bool IsConnected
58
//should we do connection sharing here?
59
public static Connection Open (string address)
61
Connection conn = new Connection ();
62
conn.OpenPrivate (address);
68
internal void OpenPrivate (string address)
71
throw new ArgumentNullException ("address");
73
AddressEntry[] entries = Address.Parse (address);
74
if (entries.Length == 0)
75
throw new Exception ("No addresses were found");
77
//TODO: try alternative addresses if needed
78
AddressEntry entry = entries[0];
80
transport = Transport.Create (entry);
82
//TODO: clean this bit up
83
ns = transport.Stream;
88
if (transport != null)
89
transport.WriteCred ();
91
SaslClient auth = new SaslClient (this);
93
isAuthenticated = true;
96
bool isAuthenticated = false;
97
internal bool IsAuthenticated
100
return isAuthenticated;
104
//Interlocked.Increment() handles the overflow condition for uint correctly, so it's ok to store the value as an int but cast it to uint
106
uint GenerateSerial ()
109
return (uint)Interlocked.Increment (ref serial);
112
internal Message SendWithReplyAndBlock (Message msg)
114
PendingCall pending = SendWithReply (msg);
115
return pending.Reply;
118
internal PendingCall SendWithReply (Message msg)
120
msg.ReplyExpected = true;
121
msg.Header.Serial = GenerateSerial ();
123
//TODO: throttle the maximum number of concurrent PendingCalls
124
PendingCall pending = new PendingCall (this);
125
pendingCalls[msg.Header.Serial] = pending;
132
internal uint Send (Message msg)
134
msg.Header.Serial = GenerateSerial ();
138
//Outbound.Enqueue (msg);
142
return msg.Header.Serial;
145
object writeLock = new object ();
146
internal void WriteMessage (Message msg)
148
byte[] HeaderData = msg.GetHeaderData ();
150
long msgLength = HeaderData.Length + (msg.Body != null ? msg.Body.Length : 0);
151
if (msgLength > Protocol.MaxMessageLength)
152
throw new Exception ("Message length " + msgLength + " exceeds maximum allowed " + Protocol.MaxMessageLength + " bytes");
155
ns.Write (HeaderData, 0, HeaderData.Length);
156
if (msg.Body != null && msg.Body.Length != 0)
157
ns.Write (msg.Body, 0, msg.Body.Length);
161
Queue<Message> Inbound = new Queue<Message> ();
163
Queue<Message> Outbound = new Queue<Message> ();
167
//should just iterate the enumerator here
168
while (Outbound.Count != 0) {
169
Message msg = Outbound.Dequeue ();
174
public bool ReadWrite (int timeout_milliseconds)
181
public bool ReadWrite ()
183
return ReadWrite (-1);
186
public bool Dispatch ()
189
Message msg = Inbound.Dequeue ();
190
//HandleMessage (msg);
195
public bool ReadWriteDispatch (int timeout_milliseconds)
201
public bool ReadWriteDispatch ()
203
return ReadWriteDispatch (-1);
207
internal Message ReadMessage ()
214
//16 bytes is the size of the fixed part of the header
215
byte[] hbuf = new byte[16];
216
read = ns.Read (hbuf, 0, 16);
222
throw new Exception ("Header read length mismatch: " + read + " of expected " + "16");
224
EndianFlag endianness = (EndianFlag)hbuf[0];
225
MessageReader reader = new MessageReader (endianness, hbuf);
227
//discard the endian byte as we've already read it
230
//discard message type and flags, which we don't care about here
234
byte version = reader.ReadByte ();
236
if (version < Protocol.MinVersion || version > Protocol.MaxVersion)
237
throw new NotSupportedException ("Protocol version '" + version.ToString () + "' is not supported");
239
if (Protocol.Verbose)
240
if (version != Protocol.Version)
241
Console.Error.WriteLine ("Warning: Protocol version '" + version.ToString () + "' is not explicitly supported but may be compatible");
243
uint bodyLength = reader.ReadUInt32 ();
245
reader.ReadUInt32 ();
246
uint headerLength = reader.ReadUInt32 ();
248
//this check may become relevant if a future version of the protocol allows larger messages
250
if (bodyLength > Int32.MaxValue || headerLength > Int32.MaxValue)
251
throw new NotImplementedException ("Long messages are not yet supported");
254
int bodyLen = (int)bodyLength;
255
int toRead = (int)headerLength;
257
//we fixup to include the padding following the header
258
toRead = Protocol.Padded (toRead, 8);
260
long msgLength = toRead + bodyLen;
261
if (msgLength > Protocol.MaxMessageLength)
262
throw new Exception ("Message length " + msgLength + " exceeds maximum allowed " + Protocol.MaxMessageLength + " bytes");
264
header = new byte[16 + toRead];
265
Array.Copy (hbuf, header, 16);
267
read = ns.Read (header, 16, toRead);
270
throw new Exception ("Message header length mismatch: " + read + " of expected " + toRead);
274
body = new byte[bodyLen];
275
read = ns.Read (body, 0, bodyLen);
278
throw new Exception ("Message body length mismatch: " + read + " of expected " + bodyLen);
281
Message msg = new Message ();
282
msg.Connection = this;
284
msg.SetHeaderData (header);
290
internal void DispatchSignals ()
293
while (Inbound.Count != 0) {
294
Message msg = Inbound.Dequeue ();
300
internal Thread mainThread = Thread.CurrentThread;
303
public void Iterate ()
305
mainThread = Thread.CurrentThread;
307
//Message msg = Inbound.Dequeue ();
308
Message msg = ReadMessage ();
313
internal void HandleMessage (Message msg)
315
//TODO: support disconnection situations properly and move this check elsewhere
317
throw new ArgumentNullException ("msg", "Cannot handle a null message; maybe the bus was disconnected");
321
if (msg.Header.Fields.TryGetValue (FieldCode.ReplySerial, out field_value)) {
322
uint reply_serial = (uint)field_value;
325
if (pendingCalls.TryGetValue (reply_serial, out pending)) {
326
if (pendingCalls.Remove (reply_serial))
332
//we discard reply messages with no corresponding PendingCall
333
if (Protocol.Verbose)
334
Console.Error.WriteLine ("Unexpected reply message received: MessageType='" + msg.Header.MessageType + "', ReplySerial=" + reply_serial);
340
switch (msg.Header.MessageType) {
341
case MessageType.MethodCall:
342
MethodCall method_call = new MethodCall (msg);
343
HandleMethodCall (method_call);
345
case MessageType.Signal:
346
//HandleSignal (msg);
348
Inbound.Enqueue (msg);
350
case MessageType.Error:
351
//TODO: better exception handling
352
Error error = new Error (msg);
353
string errMsg = String.Empty;
354
if (msg.Signature.Value.StartsWith ("s")) {
355
MessageReader reader = new MessageReader (msg);
356
errMsg = reader.ReadString ();
358
//throw new Exception ("Remote Error: Signature='" + msg.Signature.Value + "' " + error.ErrorName + ": " + errMsg);
359
//if (Protocol.Verbose)
360
Console.Error.WriteLine ("Remote Error: Signature='" + msg.Signature.Value + "' " + error.ErrorName + ": " + errMsg);
362
case MessageType.Invalid:
364
throw new Exception ("Invalid message received: MessageType='" + msg.Header.MessageType + "'");
368
Dictionary<uint,PendingCall> pendingCalls = new Dictionary<uint,PendingCall> ();
370
//this might need reworking with MulticastDelegate
371
internal void HandleSignal (Message msg)
373
Signal signal = new Signal (msg);
375
//TODO: this is a hack, not necessary when MatchRule is complete
376
MatchRule rule = new MatchRule ();
377
rule.MessageType = MessageType.Signal;
378
rule.Interface = signal.Interface;
379
rule.Member = signal.Member;
380
rule.Path = signal.Path;
383
if (Handlers.TryGetValue (rule, out dlg)) {
384
//dlg.DynamicInvoke (GetDynamicValues (msg));
386
MethodInfo mi = dlg.Method;
387
//signals have no return value
388
dlg.DynamicInvoke (MessageHelper.GetDynamicValues (msg, mi.GetParameters ()));
391
//TODO: how should we handle this condition? sending an Error may not be appropriate in this case
392
if (Protocol.Verbose)
393
Console.Error.WriteLine ("Warning: No signal handler for " + signal.Member);
397
internal Dictionary<MatchRule,Delegate> Handlers = new Dictionary<MatchRule,Delegate> ();
400
internal void MaybeSendUnknownMethodError (MethodCall method_call)
402
Message msg = MessageHelper.CreateUnknownMethodError (method_call);
407
//not particularly efficient and needs to be generalized
408
internal void HandleMethodCall (MethodCall method_call)
410
//TODO: Ping and Introspect need to be abstracted and moved somewhere more appropriate once message filter infrastructure is complete
412
//FIXME: these special cases are slightly broken for the case where the member but not the interface is specified in the message
413
if (method_call.Interface == "org.freedesktop.DBus.Peer" && method_call.Member == "Ping") {
414
object[] pingRet = new object[0];
415
Message reply = MessageHelper.ConstructReplyFor (method_call, pingRet);
420
if (method_call.Interface == "org.freedesktop.DBus.Introspectable" && method_call.Member == "Introspect") {
421
Introspector intro = new Introspector ();
422
intro.root_path = method_call.Path;
425
//FIXME: do this properly
426
//this is messy and inefficient
427
List<string> linkNodes = new List<string> ();
428
int depth = method_call.Path.Decomposed.Length;
429
foreach (ObjectPath pth in RegisteredObjects.Keys) {
430
if (pth.Value == (method_call.Path.Value)) {
431
ExportObject exo = (ExportObject)RegisteredObjects[pth];
432
intro.WriteType (exo.obj.GetType ());
434
for (ObjectPath cur = pth ; cur != null ; cur = cur.Parent) {
435
if (cur.Value == method_call.Path.Value) {
436
string linkNode = pth.Decomposed[depth];
437
if (!linkNodes.Contains (linkNode)) {
438
intro.WriteNode (linkNode);
439
linkNodes.Add (linkNode);
448
object[] introRet = new object[1];
449
introRet[0] = intro.xml;
450
Message reply = MessageHelper.ConstructReplyFor (method_call, introRet);
456
if (RegisteredObjects.TryGetValue (method_call.Path, out bo)) {
457
ExportObject eo = (ExportObject)bo;
458
eo.HandleMethodCall (method_call);
460
MaybeSendUnknownMethodError (method_call);
464
Dictionary<ObjectPath,BusObject> RegisteredObjects = new Dictionary<ObjectPath,BusObject> ();
466
//FIXME: this shouldn't be part of the core API
467
//that also applies to much of the other object mapping code
469
public object GetObject (Type type, string bus_name, ObjectPath path)
472
// return GetObject (bus_name, path);
474
//if the requested type is an interface, we can implement it efficiently
475
//otherwise we fall back to using a transparent proxy
476
if (type.IsInterface) {
477
return BusObject.GetObject (this, bus_name, path, type);
479
if (Protocol.Verbose)
480
Console.Error.WriteLine ("Warning: Note that MarshalByRefObject use is not recommended; for best performance, define interfaces");
482
BusObject busObject = new BusObject (this, bus_name, path);
483
DProxy prox = new DProxy (busObject, type);
484
return prox.GetTransparentProxy ();
488
public T GetObject<T> (string bus_name, ObjectPath path)
490
return (T)GetObject (typeof (T), bus_name, path);
493
public void Register (string bus_name, ObjectPath path, object obj)
495
ExportObject eo = new ExportObject (this, bus_name, path, obj);
496
eo.Registered = true;
498
//TODO: implement some kind of tree data structure or internal object hierarchy. right now we are ignoring the name and putting all object paths in one namespace, which is bad
499
RegisteredObjects[path] = eo;
502
public object Unregister (string bus_name, ObjectPath path)
504
//TODO: make use of bus_name
508
if (!RegisteredObjects.TryGetValue (path, out bo))
509
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
511
RegisteredObjects.Remove (path);
513
ExportObject eo = (ExportObject)bo;
514
eo.Registered = false;
519
//these look out of place, but are useful
520
internal protected virtual void AddMatch (string rule)
524
internal protected virtual void RemoveMatch (string rule)
530
if (BitConverter.IsLittleEndian)
531
NativeEndianness = EndianFlag.Little;
533
NativeEndianness = EndianFlag.Big;
536
internal static readonly EndianFlag NativeEndianness;