summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authordom96 <dominikpicheta@googlemail.com>2011-10-30 18:25:33 +0000
committerdom96 <dominikpicheta@googlemail.com>2011-10-30 18:25:33 +0000
commitf5616bcb0b0fdc35b712146166f5403f8ecc0f55 (patch)
treeab34277761a6ff72f2268952fd76af763923f4ea /lib/pure
parent299390a585a1f626c51b932484fa3929102896d7 (diff)
downloadNim-f5616bcb0b0fdc35b712146166f5403f8ecc0f55.tar.gz
Fixed issue with some functions in the sockets module not reporting the correct error message. Added more disconnection cases in the irc module, also added a message limiting system.
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/irc.nim101
-rwxr-xr-xlib/pure/sockets.nim15
2 files changed, 85 insertions, 31 deletions
diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim
index 7e1cd0eca..43687a74f 100644
--- a/lib/pure/irc.nim
+++ b/lib/pure/irc.nim
@@ -39,6 +39,8 @@ type
     lastPong: float
     lag: float
     channelsToJoin: seq[string]
+    msgLimit: bool
+    messageBuffer: seq[tuple[timeToSend: float, m: string]]
 
   TIRCMType* = enum
     MUnknown,
@@ -54,7 +56,8 @@ type
     MNick,
     MNotice,
     MPing,
-    MPong
+    MPong,
+    MError
   
   TIRCEventType* = enum
     EvMsg, EvDisconnected
@@ -66,16 +69,27 @@ type
       nick*, user*, host*, servername*: string
       numeric*: string
       params*: seq[string]
+      origin*: string ## The channel/user that this msg originated from
       raw*: string
   
-proc send*(irc: var TIRC, message: string) =
+proc send*(irc: var TIRC, message: string, sendImmediately = false) =
   ## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
-  try:
-    irc.sock.send(message & "\c\L")
-  except EOS:
-    # Assuming disconnection of every EOS could be bad,
-    # but I can't exactly check for EBrokenPipe.
-    irc.connected = false
+  var sendMsg = true
+  if irc.msgLimit and not sendImmediately:
+    var timeToSend = epochTime()
+    if irc.messageBuffer.len() >= 3:
+      timeToSend = (irc.messageBuffer[irc.messageBuffer.len()-1][0] + 2.0)
+
+    irc.messageBuffer.add((timeToSend, message))
+    sendMsg = false
+
+  if sendMsg:
+    try:
+      irc.sock.send(message & "\c\L")
+    except EOS:
+      # Assuming disconnection of every EOS could be bad,
+      # but I can't exactly check for EBrokenPipe.
+      irc.connected = false
 
 proc privmsg*(irc: var TIRC, target, message: string) =
   ## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
@@ -147,6 +161,7 @@ proc parseMessage(msg: string): TIRCEvent =
     of "QUIT": result.cmd = MQuit
     of "NICK": result.cmd = MNick
     of "NOTICE": result.cmd = MNotice
+    of "ERROR": result.cmd = MError
     else: result.cmd = MUnknown
   
   # Don't skip space here. It is skipped in the following While loop.
@@ -172,17 +187,20 @@ proc connect*(irc: var TIRC) =
   
   irc.sock = socket()
   irc.sock.connect(irc.address, irc.port)
+ 
+  irc.connected = true
   
   # Greet the server :)
-  if irc.serverPass != "": irc.send("PASS " & irc.serverPass)
-  irc.send("NICK " & irc.nick)
-  irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname])
+  if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
+  irc.send("NICK " & irc.nick, true)
+  irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
 
 proc irc*(address: string, port: TPort = 6667.TPort,
          nick = "NimrodBot",
          user = "NimrodBot",
          realname = "NimrodBot", serverPass = "",
-         joinChans: seq[string] = @[]): TIRC =
+         joinChans: seq[string] = @[],
+         msgLimit: bool = true): TIRC =
   ## This function calls `connect`, so you don't need to.
   result.address = address
   result.port = port
@@ -194,6 +212,8 @@ proc irc*(address: string, port: TPort = 6667.TPort,
   result.lastPong = -1.0
   result.lag = -1.0
   result.channelsToJoin = joinChans
+  result.msgLimit = msgLimit
+  result.messageBuffer = @[]
 
   result.connect()
   
@@ -214,6 +234,14 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
         ev.typ = EvDisconnected
       else:
         ev = parseMessage(line.string)
+        # Get the origin
+        ev.origin = ev.params[0]
+        if ev.origin == irc.nick: ev.origin = ev.nick
+
+        if ev.cmd == MError:
+          ev.typ = EvDisconnected
+          return
+
         if ev.cmd == MPing:
           irc.send("PONG " & ev.params[0])
         if ev.cmd == MPong:
@@ -227,7 +255,20 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
 
   if epochTime() - irc.lastPing >= 20.0:
     irc.lastPing = epochTime()
-    irc.send("PING :" & formatFloat(irc.lastPing))
+    irc.send("PING :" & formatFloat(irc.lastPing), true)
+
+  if epochTime() - irc.lastPong >= 120.0 and irc.lastPong != -1.0:
+    ev.typ = EvDisconnected # TODO: EvTimeout?
+    return true
+
+  
+  for i in 0..irc.messageBuffer.len-1:
+    if epochTime() >= irc.messageBuffer[0][0]:
+      irc.send(irc.messageBuffer[0].m, true)
+      irc.messageBuffer.delete(0)
+    else:
+      break # messageBuffer is guaranteed to be from the quickest to the
+            # later-est.
 
 proc getLag*(irc: var TIRC): float =
   ## Returns the latency between this client and the IRC server in seconds.
@@ -235,17 +276,18 @@ proc getLag*(irc: var TIRC): float =
   ## If latency is unknown, returns -1.0.
   return irc.lag
 
-proc getLastPong*(irc: var TIRC): float =
-  ## Returns the last time the server has responded to a PING message.
-  ##
-  ## This is useful if you want to detect whether your 
-  ## connection has timed out.
-  ## 
-  ## If a PONG has never been received, returns -1.0.
-  return irc.lastPong
- 
+proc isConnected*(irc: var TIRC): bool =
+  ## Returns whether this IRC client is connected to an IRC server.
+  return irc.connected
+
 when isMainModule:
-  var client = irc("irc.freenode.net", nick="TestBot", joinChans = @["#nimrod"])
+  #var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
+  #echo(repr(m))
+
+  #discard """
+  
+  var client = irc("amber.tenthbit.net", nick="TestBot1234",
+                   joinChans = @["#flood"])
   while True:
     var event: TIRCEvent
     if client.poll(event):
@@ -255,8 +297,11 @@ when isMainModule:
       of EvMsg:
         if event.cmd == MPrivMsg:
           var msg = event.params[event.params.high]
-          if msg == "|test": client.privmsg(event.params[0], "hello")
-          
-        echo( repr(event) )
-      echo("Lag: ", formatFloat(client.getLag()))
-      echo("Last pong: ", formatFloat(client.getLastPong()))
+          if msg == "|test": client.privmsg(event.origin, "hello")
+          if msg == "|excessFlood":
+            for i in 0..10:
+              client.privmsg(event.origin, "TEST" & $i)
+
+        #echo( repr(event) )
+      #echo("Lag: ", formatFloat(client.getLag()))
+  #"""
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index e1851301e..981afb5ca 100755
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -175,6 +175,15 @@ proc parseIp4*(s: string): int32 =
   if s[i] != '\0': invalidIp4(s)
   result = int32(a shl 24 or b shl 16 or c shl 8 or d)
 
+template gaiNim(a, p, h, l: expr): stmt =
+  block:
+    var gaiResult = getAddrInfo(a, $p, addr(h), l)
+    if gaiResult != 0'i32:
+      when defined(windows):
+        OSError()
+      else:
+        OSError($gai_strerror(gaiResult))
+
 proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
   ## binds an address/port number to a socket.
   ## Use address string in dotted decimal form like "a.b.c.d"
@@ -197,7 +206,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
     hints.ai_family = toInt(AF_INET)
     hints.ai_socktype = toInt(SOCK_STREAM)
     hints.ai_protocol = toInt(IPPROTO_TCP)
-    if getAddrInfo(address, $port, addr(hints), aiList) != 0'i32: OSError()
+    gaiNim(address, port, hints, aiList)
     if bindSocket(cint(socket), aiList.ai_addr, aiList.ai_addrLen) < 0'i32:
       OSError()
 
@@ -355,7 +364,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
   hints.ai_family = toInt(af)
   hints.ai_socktype = toInt(SOCK_STREAM)
   hints.ai_protocol = toInt(IPPROTO_TCP)
-  if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
+  gaiNim(name, port, hints, aiList)
   # try all possibilities:
   var success = false
   var it = aiList
@@ -391,7 +400,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
   hints.ai_family = toInt(af)
   hints.ai_socktype = toInt(SOCK_STREAM)
   hints.ai_protocol = toInt(IPPROTO_TCP)
-  if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
+  gaiNim(name, port, hints, aiList)
   # try all possibilities:
   var success = false
   var it = aiList