diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/impure/ssl.nim | 4 | ||||
-rw-r--r-- | lib/pure/scgi.nim | 140 | ||||
-rw-r--r-- | lib/pure/smtp.nim | 74 |
3 files changed, 187 insertions, 31 deletions
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim index c1b8276f5..e90488a2b 100644 --- a/lib/impure/ssl.nim +++ b/lib/impure/ssl.nim @@ -13,7 +13,7 @@ import openssl, strutils, os type - TSecureSocket* = object {.final.} + TSecureSocket* {.final.} = object ssl: PSSL bio: PBIO @@ -50,7 +50,7 @@ proc connect*(sock: var TSecureSocket, address: string, result = SSL_get_verify_result(sock.ssl) -proc recvLine*(sock: TSecureSocket, line: var String): bool = +proc recvLine*(sock: TSecureSocket, line: var string): bool = ## Acts in a similar fashion to the `recvLine` in the sockets module. ## Returns false when no data is available to be read. ## `Line` must be initialized and not nil! diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim new file mode 100644 index 000000000..a203cf14c --- /dev/null +++ b/lib/pure/scgi.nim @@ -0,0 +1,140 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements helper procs for SCGI applictions. Example: +## +## .. code-block:: Nimrod +## +## import strtabs, sockets, scgi +## +## var counter = 0 +## proc handleRequest(client: TSocket, input: string, +## headers: PStringTable): bool {.procvar.} = +## inc(counter) +## client.writeStatusOkTextContent() +## client.send("Hello for the $#th time." % $counter & "\c\L") +## return false # do not stop processing +## +## run(handleRequest) +## + +import sockets, strutils, os, strtabs + +type + EScgi* = object of EIO ## the exception that is raised, if a SCGI error occurs + +proc scgiError*(msg: string) {.noreturn.} = + ## raises an EScgi exception with message `msg`. + var e: ref EScgi + new(e) + e.msg = msg + raise e + +proc parseWord(inp: string, outp: var string, start: int): int = + result = start + while inp[result] != '\0': inc(result) + outp = copy(inp, start, result-1) + +proc parseHeaders(s: string, L: int): PStringTable = + result = newStringTable() + var i = 0 + while i < L: + var key, val: string + i = parseWord(s, key, i)+1 + i = parseWord(s, val, i)+1 + result[key] = val + if s[i] == ',': inc(i) + else: scgiError("',' after netstring expected") + +proc recvChar(s: TSocket): char = + var c: char + if recv(s, addr(c), sizeof(c)) == sizeof(c): + result = c + +type + TScgiState* {.final.} = object ## SCGI state object + server: TSocket + bufLen: int + client*: TSocket ## the client socket to send data to + headers*: PStringTable ## the parsed headers + input*: string ## the input buffer + +proc recvBuffer(s: var TScgiState, L: int) = + if L > s.bufLen: + s.bufLen = L + s.input = newString(L) + if L > 0 and recv(s.client, cstring(s.input), L) != L: + scgiError("could not read all data") + setLen(s.input, L) + +proc open*(s: var TScgiState, port = TPort(4000)) = + ## opens a connection. + s.bufLen = 4000 + s.input = newString(s.buflen) # will be reused + system.stackTraceNewLine = "<br />\n" + + s.server = socket() + if s.server == InvalidSocket: scgiError("could not open socket") + #s.server.connect(connectionName, port) + bindAddr(s.server, port) + listen(s.server) + +proc close*(s: var TScgiState) = + ## closes the connection. + s.server.close() + +proc next*(s: var TScgistate) = + ## proceed to the first/next request. + s.client = accept(s.server) + var L = 0 + while true: + var d = s.client.recvChar() + if d notin strutils.digits: + if d != ':': scgiError("':' after length expected") + break + L = L * 10 + ord(d) - ord('0') + recvBuffer(s, L+1) + s.headers = parseHeaders(s.input, L) + if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected") + L = parseInt(s.headers["CONTENT_LENGTH"]) + recvBuffer(s, L) + +proc writeStatusOkTextContent*(c: TSocket) = + ## sends the following string to the socket `c`:: + ## + ## Status: 200 OK\r\LContent-Type: text/plain\r\L\r\L + ## + ## You should send this before sending your HTML page, for example. + c.send("Status: 200 OK\r\L" & + "Content-Type: text/plain\r\L\r\L") + +proc run*(handleRequest: proc (client: TSocket, input: string, + headers: PStringTable): bool, + port = TPort(4000)) = + ## encapsulates the SCGI object and main loop. + var s: TScgiState + s.open(port) + var stop = false + while not stop: + next(s) + stop = handleRequest(s.client, s.input, s.headers) + s.client.close() + s.close() + +when isMainModule: + var counter = 0 + proc handleRequest(client: TSocket, input: string, + headers: PStringTable): bool {.procvar.} = + inc(counter) + client.writeStatusOkTextContent() + client.send("Hello for the $#th time." % $counter & "\c\L") + return false # do not stop processing + + run(handleRequest) + diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index dcec012eb..88c05aaed 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -10,7 +10,8 @@ ## This module implements the SMTP client protocol as specified by RFC 5321, ## this can be used to send mail to any SMTP Server. ## -## This module also implements the protocol used to format messages, as specified by RFC 2822. +## This module also implements the protocol used to format messages, +## as specified by RFC 2822. ## ## Example gmail use: ## @@ -23,40 +24,49 @@ ## smtp.auth("username", "password") ## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) ## +## +## For SSL support this module relies on the SSL module. If you want to +## disable SSL, compile with ``-d:NoSSL``. + +import sockets, strutils, strtabs, base64, os -import sockets, strutils, strtabs, ssl, base64, os +when not defined(noSSL): + import ssl type - TSMTP* = object {.final.} + TSMTP* {.final.} = object sock: TSocket - sslSock: TSecureSocket + when not defined(noSSL): + sslSock: TSecureSocket ssl: Bool debug: Bool - TMessage* = object {.final.} - msgTo: seq[String] - msgCc: seq[String] - msgSubject: String + TMessage* {.final.} = object + msgTo: seq[string] + msgCc: seq[string] + msgSubject: string msgOtherHeaders: PStringTable - msgBody: String + msgBody: string EInvalidReply* = object of EBase -proc debugSend(smtp: TSMTP, cmd: String) = +proc debugSend(smtp: TSMTP, cmd: string) = if smtp.debug: echo("C:" & cmd) if not smtp.ssl: smtp.sock.send(cmd) else: - smtp.sslSock.send(cmd) + when not defined(noSSL): + smtp.sslSock.send(cmd) -proc debugRecv(smtp: TSMTP): String = +proc debugRecv(smtp: TSMTP): string = var line = "" var ret = False if not smtp.ssl: ret = smtp.sock.recvLine(line) else: - ret = smtp.sslSock.recvLine(line) + when not defined(noSSL): + ret = smtp.sslSock.recvLine(line) if ret: if smtp.debug: echo("S:" & line) @@ -65,7 +75,7 @@ proc debugRecv(smtp: TSMTP): String = OSError() return "" -proc quitExcpt(smtp: TSMTP, msg: String) = +proc quitExcpt(smtp: TSMTP, msg: string) = smtp.debugSend("QUIT") raise newException(EInvalidReply, msg) @@ -74,8 +84,8 @@ proc checkReply(smtp: TSMTP, reply: string) = if not line.startswith(reply): quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) -proc connect*(address: String, port: int = 25, - ssl: bool = False, debug: bool = False): TSMTP = +proc connect*(address: string, port = 25, + ssl = false, debug = false): TSMTP = ## Establishes a connection with a SMTP server. ## May fail with EInvalidReply or with a socket errors. @@ -83,9 +93,13 @@ proc connect*(address: String, port: int = 25, result.sock = socket() result.sock.connect(address, TPort(port)) else: - result.ssl = True - discard result.sslSock.connect(address, port) - + when not defined(noSSL): + result.ssl = True + discard result.sslSock.connect(address, port) + else: + raise newException(EInvalidReply, + "SMTP module compiled without SSL support") + result.debug = debug result.checkReply("220") @@ -93,7 +107,8 @@ proc connect*(address: String, port: int = 25, result.checkReply("250") proc auth*(smtp: TSMTP, username, password: string) = - ## Sends an AUTH command to the server to login as the `username` using `password`. + ## Sends an AUTH command to the server to login as the `username` + ## using `password`. ## May fail with EInvalidReply. smtp.debugSend("AUTH LOGIN\c\L") @@ -108,7 +123,8 @@ proc auth*(smtp: TSMTP, username, password: string) = proc sendmail*(smtp: TSMTP, fromaddr: string, toaddrs: seq[string], msg: string) = ## Sends `msg` from `fromaddr` to `toaddr`. - ## Messages may be formed using ``createMessage`` by converting the TMessage into a string. + ## Messages may be formed using ``createMessage`` by converting the + ## TMessage into a string. smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L") smtp.checkReply("250") @@ -126,8 +142,8 @@ proc sendmail*(smtp: TSMTP, fromaddr: string, # quit smtp.debugSend("QUIT\c\L") -proc createMessage*(mSubject, mBody: String, mTo, mCc: seq[String], - otherHeaders: openarray[tuple[name, value: String]]): TMessage = +proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], + otherHeaders: openarray[tuple[name, value: string]]): TMessage = ## Creates a new MIME compliant message. result.msgTo = mTo result.msgCc = mCc @@ -137,8 +153,8 @@ proc createMessage*(mSubject, mBody: String, mTo, mCc: seq[String], for n, v in items(otherHeaders): result.msgOtherHeaders[n] = v -proc createMessage*(mSubject, mBody: String, mTo, - mCc: seq[String] = @[]): TMessage = +proc createMessage*(mSubject, mBody: string, mTo, + mCc: seq[string] = @[]): TMessage = ## Alternate version of the above. result.msgTo = mTo result.msgCc = mCc @@ -146,18 +162,19 @@ proc createMessage*(mSubject, mBody: String, mTo, result.msgBody = mBody result.msgOtherHeaders = newStringTable() -proc `$`*(msg: TMessage): String = +proc `$`*(msg: TMessage): string = result = "" if msg.msgTo.len() > 0: result = "TO: " & msg.msgTo.join(", ") & "\c\L" if msg.msgCc.len() > 0: - result.add("CC: " & msg.msgTo.join(", ") & "\c\L") + result.add("CC: " & msg.msgCc.join(", ") & "\c\L") # TODO: Folding? i.e when a line is too long, shorten it... result.add("Subject: " & msg.msgSubject & "\c\L") for key, value in pairs(msg.msgOtherHeaders): result.add(key & ": " & value & "\c\L") - result.add("\c\L" & msg.msgBody) + result.add("\c\L") + result.add(msg.msgBody) when isMainModule: @@ -169,7 +186,6 @@ when isMainModule: #smtp.sendmail("root@localhost", @["dominik@localhost"], $msg) #echo(decode("a17sm3701420wbe.12")) - var msg = createMessage("Hello from Nimrod's SMTP!", "Hello!!!!.\n Is this awesome or what?", @["someone@yahoo.com", "someone@gmail.com"]) |