Tcl imap client

Tcl imap client

Post by John Kell » Fri, 14 Sep 2007 23:18:55


ere is a Tcl script implementing an imap client using non-blocking
I/O on all channels, including stdin. It's very simple minded, you
only get raw imap protocol.

The cmdcount variable is used for creating the leading tag of each
imap command. You don't have to type it yourself, the script does it
for you. You just enter the text of the imap command.

What's interesting in the interaction between the stdin fileevent and
the socket output fileevent.

When data is available on stdin, getuser wakes up. But you don't want
to write that data to the socket yet, because you need to know whether
the socket is ready to accept data.

It's simple: you disable getuser, the stdin event (which you just
entered), and enable putsocket, the socket writable event. Then you
just return back to the event loop.

Most likely, the socket is already writable, and putsocket will wake
up immediately. But if there is some delay, that's OK too.

Once you're in putsocket, you know the socket is ready to accept data.
But first, you must disable the socket writable event (which you just
entered), or it will keep firing as long as the socket can accept even
1 byte of data, causing infinitely repeating invocations of putsocket,
which is very undesirable! You also reenable the stdin readable event
to prepare for the next cycle. Then, you write your user data to the
socket, return to the event loop, and the cycle is complete. It will
begin again, as soon as more user input is available from stdin.

The structural elements in this example should be readily adaptable to
any network client.

I hereby release this original work to the public domain. Credit also
goes to David Gravereaux for the trick of initializing the socket with
a writable event instead of a readable event, so the script will work
on Windows too.

Enjoy!


set host localhost
set port 143
set initlimit 5000
set cmdcount 100

proc getuser {sock} {

fileevent stdin readable {}
fileevent $sock writable [list putsocket $sock]

return
}

proc putsocket {sock} {

fileevent $sock writable {}
fileevent stdin readable [list getuser $sock]

global cmdcount
incr cmdcount

gets stdin line
set exception [catch {puts $sock "c$cmdcount $line"} data]

if {$exception} {
puts $data
global mainevent
set mainevent putsfailure

} else {
flush $sock
}

return
}

proc getsocket {sock} {

set exception [catch {gets $sock line} data]

if {$exception} {
puts $data
global mainevent
set mainevent getsfailure

} else {
if {!($data < 0)} {
puts $line
} elseif {[fblocked $sock]} {
# Nothing to do
}
if {[eof $sock]} {
global mainevent
set mainevent sockclosed
}
}

return
}

proc initsocket {sock} {

fileevent $sock writable {}

global initalarm
after cancel $initalarm

set initerror [fconfigure $sock -error]

if {[string length $initerror]} {
puts $initerror
global mainevent
set mainevent sockfailure
} else {
fconfigure stdin -blocking false
fileevent stdin readable [list getuser $sock]
fileevent $sock readable [list getsocket $sock]
}

return
}

set sock [socket -async $host $port]
fconfigure $sock -blocking fal
 
 
 

Tcl imap client

Post by John Kell » Sun, 16 Sep 2007 04:09:56

On Thu, 13 Sep 2007 14:18:55 +0000, John Kelly < XXXX@XXXXX.COM >



After testing, I see it gets confused with a multi line command like
IDLE. So included below, is a patch to fix that up.

It only understands one composite (multi-line) command, namely IDLE,
but extending the script to handle others, should not be hard.

Tcl, the too cool language, heh. Maybe the script could be extended
with expect to do protcol testing. The possibilities are interesting.

Here's the patch:


--- imapclient.v1.tcl 2007-09-13 09:23:54.000000000 -0400
+++ imapclient.v2.tcl 2007-09-14 09:00:18.000000000 -0400
@@ -1,7 +1,12 @@
+# s simple
+# c composite
+# tscc tag sequence of composite command
+# plus = tscc, after "+" command continuation response from server
+
set host localhost
set port 143
set initlimit 5000
-set cmdcount 100
+set sequence 100

proc getuser {sock} {

@@ -16,11 +21,28 @@
fileevent $sock writable {}
fileevent stdin readable [list getuser $sock]

- global cmdcount
- incr cmdcount
+ global sequence
+ global tscc plus

gets stdin line
- set exception [catch {puts $sock "c$cmdcount $line"} data]
+
+ set prefix s
+ switch [lindex $line 0] {
+ idle {
+ set prefix c
+ }
+ }
+
+ set toksp {}
+ if {!$plus} {
+ incr sequence
+ set toksp "$prefix$sequence "
+ if {$prefix eq "c"} {
+ set tscc $sequence
+ }
+ }
+
+ set exception [catch {puts $sock $toksp$line} data]

if {$exception} {
puts $data
@@ -36,6 +58,8 @@

proc getsocket {sock} {

+ global tscc plus
+
set exception [catch {gets $sock line} data]

if {$exception} {
@@ -46,6 +70,25 @@
} else {
if {!($data < 0)} {
puts $line
+ set token [lindex $line 0]
+ switch $token {
+ "*" {
+ #
+ }
+ "+" {
+ if {$tscc} {
+ set plus $tscc
+ } else {
+ puts {unexpected "+" command continuation response from server}
+ }
+ }
+ default {
+ if {$plus && $plus == [string range $token 1 end]} {
+ set tscc 0
+ set plus 0
+ }
+ }
+ }
} elseif {[fblocked $sock]} {
# Nothing to do
}
@@ -80,6 +123,9 @@
return
}

+set tscc 0
+set plus 0
+
set sock [socket -async $host $port]
fconfigure $sock -blocking false
fileevent $sock writable [list initsocket $sock]


--
Internet service
http://www.yqcomputer.com/

 
 
 

Tcl imap client

Post by John Kell » Mon, 17 Sep 2007 05:01:21

On Fri, 14 Sep 2007 19:09:56 +0000, John Kelly < XXXX@XXXXX.COM >



And here is another patch to handle upper/lower case ...


From XXXX@XXXXX.COM Sat Sep 15 19:57:01 2007
Received: via dmail-2006k.17 for jak; Sat, 15 Sep 2007 15:57:01 -0400 (EDT)
Return-Path: < XXXX@XXXXX.COM >
Received: from daves.isp2dial.com (localhost [127.0.0.1])
by daves.isp2dial.com (Hard2Crack-0.001) with ESMTP id l8FJv1Pn003020
for < XXXX@XXXXX.COM >; Sat, 15 Sep 2007 15:57:01 -0400
Received: (from root@localhost)
by daves.isp2dial.com (8.14.1/8.14.1/Submit) id l8FJv1jd003019
for jak; Sat, 15 Sep 2007 15:57:01 -0400
From: root < XXXX@XXXXX.COM >
Date: Sat, 15 Sep 2007 15:57:01 -0400
To: XXXX@XXXXX.COM
Message-ID: < XXXX@XXXXX.COM >
User-Agent: nail 11.4 8/29/04
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Status:

--- imapclient.v2.tcl 2007-09-14 09:00:18.000000000 -0400
+++ imapclient.v3.tcl 2007-09-15 15:51:57.000000000 -0400
@@ -27,8 +27,8 @@
gets stdin line

set prefix s
- switch [lindex $line 0] {
- idle {
+ switch [string toupper [lindex $line 0]] {
+ IDLE {
set prefix c
}
}


--
Internet service
http://www.yqcomputer.com/
 
 
 

Tcl imap client

Post by John Kell » Mon, 17 Sep 2007 05:06:02

On Fri, 14 Sep 2007 19:09:56 +0000, John Kelly < XXXX@XXXXX.COM >



And here is another patch to handle upper/lower case ...


--- imapclient.v2.tcl 2007-09-14 09:00:18.000000000 -0400
+++ imapclient.v3.tcl 2007-09-15 15:51:57.000000000 -0400
@@ -27,8 +27,8 @@
gets stdin line

set prefix s
- switch [lindex $line 0] {
- idle {
+ switch [string toupper [lindex $line 0]] {
+ IDLE {
set prefix c
}
}
--
Internet service
http://www.yqcomputer.com/