|
From: creecode <icr...@us...> - 2006-03-10 21:46:45
|
Update of /cvsroot/frontierkernel/odbs/frontierRoot/system/verbs/builtins/tcp In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12025 Modified Files: sendMail Log Message: added support for STMP AUTH using CRAM-MD5, LOGIN, and PLAIN use Frontier.getProgramName for X-Mailer Index: sendMail =================================================================== RCS file: /cvsroot/frontierkernel/odbs/frontierRoot/system/verbs/builtins/tcp/sendMail,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** sendMail 6 Jul 2005 19:25:03 -0000 1.2 --- sendMail 10 Mar 2006 21:46:41 -0000 1.3 *************** *** 1,3 **** ! FrontierVcsFile:1:scpt:system.verbs.builtins.tcp.sendMail ! on sendMail (recipient="", subject="", message="", sender=user.prefs.mailAddress, cc="", bcc="", host=user.prefs.mailHost, mimeType="text/plain", adrHdrTable=nil, timeOutTicks=60*60, flMessages=true, port = user.prefs.mailHostPort) { ÇChanges Ç7/5/05; 8:57:26 AM by TAC Çremoved Userland from X-Mailer Çadded port parameter Ç11/05/1999 at 12:57:30 AM by AR ÇUpdated to use 6.1 TCP verbs. ÇCleaned up low-level error messages to conform to "Can't do x because y" scheme. Ç7/25/99; 8:04:26 AM by DW ÇLoop over scripts in user.callbacks.sendmail table. ÇWed, 16 Jun 1999 00:05:16 GMT by AR ÇAdded optional adrHdrTable parameter, the address of a table. If an address is specified, its fields are added to the message header, possibly overriding standard header items. ÇAdded optional timeOutTicks parameter. The default value is 3600 ticks (60 seconds). If the connection is idle for more than the specified number of ticks while waiting for a response from the server, the connection is closed and an error message is generated. ÇAdded optional flMessages parameter. If it is true (the default), status messages will be displayed in Frontier's About window. The displayed messages are prefixed with the name of this script. Before we return, we clean up the message area. ÇAdded extensive error checking and reporting code. If the script returns true, it is now safe to assume that the message was accepted by the server. Status messages received from the server indicating that an error has occured are no longer ignored. Whenever an error occurs, we try to shut down the connection gracefully before throwing a scripterror. ÇCalls to thread.sleepFor were added to the TCP read/write loops for improved thread-friendliness. bundle { //loop over callbacks if defined (user.callbacks.tcpSendMail) { local (i); for i = 1 to sizeof (user.callbacks.tcpSendMail) { try { local (adrcallback = @user.callbacks.tcpSendMail [i]); if typeof (adrcallback^) == scriptType { //avoid problems with item #1s if adrcallback^ (recipient, subject, message, sender, cc, bcc, mimetype) { Çcallback returns true if it consumed the sendMail operation return (true)}}}}}}; on displayMsg (s) { msg ("tcp.sendMail: " + s)}; local (buffer = "", response = ""); on add (s) { //appends crlf delimited items to our message buffer buffer = buffer + s + cr + lf}; bundle { //build the header and store in buffer local (addedFromTableList = {}); on addFromHeaderTable (s) { if (adrHdrTable != nil) and defined (adrHdrTable^.[s]) { add (nameOf (adrHdrTable^.[s]) + ": " + adrHdrTable^.[s]); addedFromTableList = addedFromTableList + string.lower (s); return (true)}; return (false)}; if not addFromHeaderTable ("X-Mailer") { add ("X-Mailer: " + "Frontier " + Frontier.version () + " (" + sys.osName () + ")")}; if not addFromHeaderTable ("Mime-Version") { add ("Mime-Version: 1.0")}; if not addFromHeaderTable ("Content-Type") { add ("Content-Type: " + mimeType + "; charset=\"us-ascii\"")}; if not addFromHeaderTable ("Date") { add ("Date: " + date.netStandardString (clock.now ()))}; if not addFromHeaderTable ("To") { if sizeOf (recipient) > 0 { add ("To: " + recipient)}}; if not addFromHeaderTable ("CC") { if sizeOf (cc) > 0 { add ("CC: " + cc)}}; if not addFromHeaderTable ("From") { add ("From: " + sender)}; if not addFromHeaderTable ("Subject") { add ("Subject: " + subject)}; if adrHdrTable != nil { //now add remaining header fields from the table local (i); for i = 1 to sizeOf (adrHdrTable^) { if not (addedFromTableList contains string.lower (nameOf (adrHdrTable^ [i]))) { add (nameOf (adrHdrTable^ [i]) + ": " + adrHdrTable^ [i])}}}; add ("")}; //a blank line before the content is required! bundle { //build message body and store in buffer if not (message contains lf) { //convert line-endings message = string.replaceAll (message, cr, cr+lf)}; message = string.replaceAll (message, "\r\n.\r\n", "\r\n..\r\n"); //replace lines beginning with a period with two periods if message beginsWith ".\r\n" { //if the first line begins with a period message = "." + message}; if not (message endsWith "\r\n") { message = message + "\r\n"}; add (message)}; //add our message text to the buffer on sendCommandToServer (stream, command) { //AR 10/27/1999 Çon sendCommandToServer (stream, command) //pre-6.1 code Çlocal (chunksize = 4 * 1024) Çlocal (lastWrite = clock.ticks ()) Çloop //write command to the stream Çtry Çif sizeof (command) <= chunkSize Çtcp.writeStream (stream, command) Çsys.systemTask() Çthread.sleepfor (0) Çbreak Çtcp.writeStream (stream, string.mid (command, 1, chunkSize)) Çcommand = string.delete (command, 1, chunkSize) Çsys.systemTask() Çthread.sleepfor (0) ÇlastWrite = clock.ticks () Çelse Çif (tryError contains "10035") and (clock.ticks () < (lastWrite + (60 * 10))) //10035 means the socket buffers are not ready, give them up to 10 seconds. Çsys.systemTask() Çthread.sleepfor (1) //yield this thread for a second to allow normal frontier processing Çelse Çtry {tcp.closeStream (stream)} ÇscriptError ("Error communicating with SMTP host: " + tryError) // re-throw the error Çreturn (true) try { tcp.writeStringToStream (stream, command, 4096, timeOutTicks / 60)} else { if flMessages { //clean up the message area msg ("")}; try {tcp.abortStream (stream)}; scriptError ("Can't send mail because a connection error occured: " + tryerror)}; return (true)}; on receiveServerResponse (stream) { //AR 10/27/1999 Çon receiveServerResponse (stream) //pre-6.1 code Çlocal (response = "", bytesPending = 0, ix, linetext) Çlocal (timeout = clock.ticks () + timeOutTicks) Çloop //read data from the stream Çcase tcp.statusStream (stream, @bytespending) Ç"DATA" //read data Çresponse = response + tcp.readStream (stream, bytespending) Çix = string.patternMatch ("\r\n", response) Çwhile (ix > 0) //handle multi-line responses Çlinetext = string.mid (response, 1, ix-1) Çif string.nthChar (linetext, 4) == ' ' //the final line of the response has a space as the fourth char Çreturn (linetext) Çresponse = string.delete (response, 1, ix+1) Çix = string.patternMatch ("\r\n", response) Çsys.systemTask() Çthread.sleepfor (0) Çtimeout = clock.ticks () + timeOutTicks Çcontinue Ç"OPEN" //waiting for data Çif clock.ticks () > timeout ÇscriptError ("Can't send mail because the connection to " + host + " timed out.") Çsys.systemTask() Çthread.sleepfor (0) Çcontinue Çelse //connection terminated prematurely Çtry {tcp.closeStream (stream)} ÇscriptError ("Can't send mail because the connection to " + host + " was terminated prematurely.") Çreturn ("") //this should never be executed, either we return the server's final response line or we throw a scripterror local (ix, linetext); loop { //read data from the stream try { tcp.readStreamUntil (stream, "\r\n", timeOutTicks / 60, @response)} else { try {tcp.abortStream (stream)}; if flMessages { //clean up the message area msg ("")}; scriptError ("Can't send mail because a connection error occured: " + tryerror)}; ix = string.patternMatch ("\r\n", response); while (ix > 0) { //handle multi-line responses linetext = string.mid (response, 1, ix-1); response = string.delete (response, 1, ix+1); if string.nthChar (linetext, 4) == ' ' { //the final line of the response has a space as the fourth char return (linetext)}; ix = string.patternMatch ("\r\n", response)}}; return ("")}; //this should never be executed, either we return the server's final response line or we throw a scripterror on executeCommand (command, expectResponse = nil) { //send a command to the server and handle the response local (statusline); sendCommandToServer (stream, command + "\r\n"); statusline = receiveServerResponse (stream); if flMessages { displayMsg (statusline)}; if (expectResponse != nil) and not (statusline beginsWith expectResponse) { closeConnection ("Can't send mail because of an unexpected server response. I said \"" + command + "\" and the server replied \"" + statusline + "\".")}; return (true)}; on addRecipient (recipient) { //send recipient command executeCommand ("RCPT TO: <" + string.cleanMailAddress (recipient) + ">", "25")}; //expect status code 250 or 251 on closeConnection (errorstring = "") { //also called in case of unexpected server responses try {executeCommand ("RSET")}; //don't expect a specific response, otherwise we might end up in an infinite loop try {executeCommand ("QUIT")}; try {tcp.closeStream (stream)}; if flMessages { //clean up the message area msg ("")}; if errorstring != "" { //possibly (re)throw scripterrors scriptError (errorstring)}}; local (stream); try { //try to open connection to server stream = tcp.openStream (host, port)} else { // fatal error scriptError ("Can't send mail because the connection to " + host + " could not be opened: " + tryError)}; bundle { //wait for server's initial connection greeting local (statusline); statusline = receiveServerResponse (stream); if flMessages { displayMsg (statusline)}; if not (statusline beginsWith "220") { closeConnection ("Can't send mail because the server, " + host + ", did not send a valid connection greeting. It said, \"" + statusline + "\".")}}; bundle { //say hello and reset state executeCommand ("HELO " + tcp.addressDecode (tcp.myAddress()), "250"); executeCommand ("RSET", "250")}; bundle { //specify sender and recipients executeCommand ("MAIL FROM: <" + string.cleanMailAddress (sender) + ">", "250"); local (i); if sizeOf (recipient) > 0 { for i = 1 to string.countFields (recipient, ',') { addRecipient (string.nthField (recipient, ',', i))}}; if sizeOf (cc) > 0 { for i = 1 to string.countFields (cc, ',') { addRecipient (string.nthField (cc, ',', i))}}; if sizeOf (bcc) > 0 { for i = 1 to string.countFields (bcc, ',') { addRecipient (string.nthField (bcc, ',', i))}}}; bundle { //send message data executeCommand ("DATA", "354"); sendCommandToServer (stream, buffer); executeCommand (".", "250")}; closeConnection (); return (true)} \ No newline at end of file --- 1,313 ---- ! FrontierVcsFile:2:scpt:system.verbs.builtins.tcp.sendMail ! on sendMail (recipient = "", subject = "", message = "", sender = user.prefs.mailAddress, cc = "", bcc = "", host = user.prefs.mailHost, mimeType = "text/plain", adrHdrTable = nil, timeOutTicks = 60 * 60, flMessages = true, port = user.prefs.mailHostPort, smtpAuth = user.prefs.mailSmtpAuth, senderPassword = user.prefs.mailPassword) { ! «Changes ! «3/6/06; 11:11:09 AM by TAC ! «added support for STMP AUTH using CRAM-MD5, LOGIN, and PLAIN ! «use Frontier.getProgramName for X-Mailer ! «7/5/05; 8:57:26 AM by TAC ! «removed Userland from X-Mailer ! «added port parameter ! «11/05/1999 at 12:57:30 AM by AR ! «Updated to use 6.1 TCP verbs. ! «Cleaned up low-level error messages to conform to "Can't do x because y" scheme. ! «7/25/99; 8:04:26 AM by DW ! «Loop over scripts in user.callbacks.sendmail table. ! «Wed, 16 Jun 1999 00:05:16 GMT by AR ! «Added optional adrHdrTable parameter, the address of a table. If an address is specified, its fields are added to the message header, possibly overriding standard header items. ! «Added optional timeOutTicks parameter. The default value is 3600 ticks (60 seconds). If the connection is idle for more than the specified number of ticks while waiting for a response from the server, the connection is closed and an error message is generated. ! «Added optional flMessages parameter. If it is true (the default), status messages will be displayed in Frontier's About window. The displayed messages are prefixed with the name of this script. Before we return, we clean up the message area. ! «Added extensive error checking and reporting code. If the script returns true, it is now safe to assume that the message was accepted by the server. Status messages received from the server indicating that an error has occured are no longer ignored. Whenever an error occurs, we try to shut down the connection gracefully before throwing a scripterror. ! «Calls to thread.sleepFor were added to the TCP read/write loops for improved thread-friendliness. ! ! bundle { //loop over callbacks ! if defined (user.callbacks.tcpSendMail) { ! local (i); ! for i = 1 to sizeof (user.callbacks.tcpSendMail) { ! try { ! local (adrcallback = @user.callbacks.tcpSendMail [i]); ! if typeof (adrcallback^) == scriptType { //avoid problems with item #1s ! if adrcallback^ (recipient, subject, message, sender, cc, bcc, mimetype) { ! «callback returns true if it consumed the sendMail operation ! return (true)}}}}}}; ! ! on displayMsg (s) { ! msg ("tcp.sendMail: " + s)}; ! ! local (buffer = "", response = ""); ! ! on add (s) { //appends crlf delimited items to our message buffer ! buffer = buffer + s + cr + lf}; ! ! bundle { //build the header and store in buffer ! local (addedFromTableList = {}); ! ! on addFromHeaderTable (s) { ! if (adrHdrTable != nil) and defined (adrHdrTable^.[s]) { ! add (nameOf (adrHdrTable^.[s]) + ": " + adrHdrTable^.[s]); ! addedFromTableList = addedFromTableList + string.lower (s); ! return (true)}; ! return (false)}; ! ! if not addFromHeaderTable ("X-Mailer") { ! add ("X-Mailer: " + Frontier.getProgramName () + " (" + sys.osName () + ")")}; ! ! if not addFromHeaderTable ("Mime-Version") { ! add ("Mime-Version: 1.0")}; ! ! if not addFromHeaderTable ("Content-Type") { ! add ("Content-Type: " + mimeType + "; charset=\"us-ascii\"")}; ! ! if not addFromHeaderTable ("Date") { ! add ("Date: " + date.netStandardString (clock.now ()))}; ! ! if not addFromHeaderTable ("To") { ! if sizeOf (recipient) > 0 { ! add ("To: " + recipient)}}; ! ! if not addFromHeaderTable ("CC") { ! if sizeOf (cc) > 0 { ! add ("CC: " + cc)}}; ! ! if not addFromHeaderTable ("From") { ! add ("From: " + sender)}; ! ! if not addFromHeaderTable ("Subject") { ! add ("Subject: " + subject)}; ! ! if adrHdrTable != nil { //now add remaining header fields from the table ! local (i); ! for i = 1 to sizeOf (adrHdrTable^) { ! if not (addedFromTableList contains string.lower (nameOf (adrHdrTable^ [i]))) { ! add (nameOf (adrHdrTable^ [i]) + ": " + adrHdrTable^ [i])}}}; ! add ("")}; //a blank line before the content is required! ! ! bundle { //build message body and store in buffer ! if not (message contains lf) { //convert line-endings ! message = string.replaceAll (message, cr, cr+lf)}; ! message = string.replaceAll (message, "\r\n.\r\n", "\r\n..\r\n"); //replace lines beginning with a period with two periods ! if message beginsWith ".\r\n" { //if the first line begins with a period ! message = "." + message}; ! if not (message endsWith "\r\n") { ! message = message + "\r\n"}; ! add (message)}; //add our message text to the buffer ! ! on sendCommandToServer (stream, command) { //AR 10/27/1999 ! «on sendCommandToServer (stream, command) //pre-6.1 code ! «local (chunksize = 4 * 1024) ! «local (lastWrite = clock.ticks ()) ! «loop //write command to the stream ! «try ! «if sizeof (command) <= chunkSize ! «tcp.writeStream (stream, command) ! «sys.systemTask() ! «thread.sleepfor (0) ! «break ! «tcp.writeStream (stream, string.mid (command, 1, chunkSize)) ! «command = string.delete (command, 1, chunkSize) ! «sys.systemTask() ! «thread.sleepfor (0) ! «lastWrite = clock.ticks () ! «else ! «if (tryError contains "10035") and (clock.ticks () < (lastWrite + (60 * 10))) //10035 means the socket buffers are not ready, give them up to 10 seconds. ! «sys.systemTask() ! «thread.sleepfor (1) //yield this thread for a second to allow normal frontier processing ! «else ! «try {tcp.closeStream (stream)} ! «scriptError ("Error communicating with SMTP host: " + tryError) // re-throw the error ! «return (true) ! try { ! tcp.writeStringToStream (stream, command, 4096, timeOutTicks / 60)} ! else { ! if flMessages { //clean up the message area ! msg ("")}; ! try {tcp.abortStream (stream)}; ! scriptError ("Can't send mail because a connection error occured: " + tryerror)}; ! return (true)}; ! ! on receiveServerResponse (stream) { //AR 10/27/1999 ! «on receiveServerResponse (stream) //pre-6.1 code ! «local (response = "", bytesPending = 0, ix, linetext) ! «local (timeout = clock.ticks () + timeOutTicks) ! «loop //read data from the stream ! «case tcp.statusStream (stream, @bytespending) ! «"DATA" //read data ! «response = response + tcp.readStream (stream, bytespending) ! «ix = string.patternMatch ("\r\n", response) ! «while (ix > 0) //handle multi-line responses ! «linetext = string.mid (response, 1, ix-1) ! «if string.nthChar (linetext, 4) == ' ' //the final line of the response has a space as the fourth char ! «return (linetext) ! «response = string.delete (response, 1, ix+1) ! «ix = string.patternMatch ("\r\n", response) ! «sys.systemTask() ! «thread.sleepfor (0) ! «timeout = clock.ticks () + timeOutTicks ! «continue ! «"OPEN" //waiting for data ! «if clock.ticks () > timeout ! «scriptError ("Can't send mail because the connection to " + host + " timed out.") ! «sys.systemTask() ! «thread.sleepfor (0) ! «continue ! «else //connection terminated prematurely ! «try {tcp.closeStream (stream)} ! «scriptError ("Can't send mail because the connection to " + host + " was terminated prematurely.") ! «return ("") //this should never be executed, either we return the server's final response line or we throw a scripterror ! local (ix, linetext); ! loop { //read data from the stream ! try { ! tcp.readStreamUntil (stream, "\r\n", timeOutTicks / 60, @response)} ! else { ! try {tcp.abortStream (stream)}; ! if flMessages { //clean up the message area ! msg ("")}; ! scriptError ("Can't send mail because a connection error occured: " + tryerror)}; ! ix = string.patternMatch ("\r\n", response); ! while (ix > 0) { //handle multi-line responses ! linetext = string.mid (response, 1, ix-1); ! response = string.delete (response, 1, ix+1); ! if string.nthChar (linetext, 4) == ' ' { //the final line of the response has a space as the fourth char ! return (linetext)}; ! ix = string.patternMatch ("\r\n", response)}}; ! return ("")}; //this should never be executed, either we return the server's final response line or we throw a scripterror ! ! on executeCommand (command, expectResponse = nil, adrResponse = nil) { //send a command to the server and handle the response ! local (statusline); ! sendCommandToServer (stream, command + "\r\n"); ! statusline = receiveServerResponse (stream); ! if flMessages { ! displayMsg (statusline)}; ! if (expectResponse != nil) and not (statusline beginsWith expectResponse) { ! closeConnection ("Can't send mail because of an unexpected server response. I said \"" + command + "\" and the server replied \"" + statusline + "\".")}; ! if adrResponse != nil { ! adrResponse^ = statusline}; ! return (true)}; ! ! on addRecipient (recipient) { //send recipient command ! executeCommand ("RCPT TO: <" + string.cleanMailAddress (recipient) + ">", "25")}; //expect status code 250 or 251 ! ! on closeConnection (errorstring = "") { //also called in case of unexpected server responses ! try {executeCommand ("RSET")}; //don't expect a specific response, otherwise we might end up in an infinite loop ! try {executeCommand ("QUIT")}; ! try {tcp.closeStream (stream)}; ! if flMessages { //clean up the message area ! msg ("")}; ! if errorstring != "" { //possibly (re)throw scripterrors ! scriptError (errorstring)}}; ! ! local (stream); ! ! try { //try to open connection to server ! stream = tcp.openStream (host, port)} ! else { // fatal error ! scriptError ("Can't send mail because the connection to " + host + " could not be opened: " + tryError)}; ! ! bundle { //wait for server's initial connection greeting ! local (statusline); ! statusline = receiveServerResponse (stream); ! if flMessages { ! displayMsg (statusline)}; ! if not (statusline beginsWith "220") { ! closeConnection ("Can't send mail because the server, " + host + ", did not send a valid connection greeting. It said, \"" + statusline + "\".")}}; ! ! bundle { //say hello and reset state ! executeCommand ("HELO " + tcp.addressDecode (tcp.myAddress()), "250"); ! executeCommand ("RSET", "250")}; ! ! local (flAuthenticated = false); ! ! smtpAuth = string.upper (smtpAuth); ! ! case smtpAuth { // authenticate ! "CRAM-MD5" { ! local (s); ! ! executeCommand ("AUTH " + smtpAuth, "334", @s); ! ! s = s - "334 "; ! s = base64.decode (s); ! ! s = string.hashCramMd5 (string.nthField (sender, '@', 1), string (senderPassword), s); ! s = base64.encode (s, 0); ! ! executeCommand (s, "235"); ! ! flAuthenticated = true}; ! "LOGIN" { ! local (s); ! ! executeCommand ("AUTH " + smtpAuth, "334", @s); ! ! bundle { // username ! s = s - "334 "; ! s = base64.decode (s); ! s = string.lower (s); ! ! if not (s == "username:") { ! scriptError ("SMTP AUTH LOGIN failed.")}; ! ! s = base64.encode (string.nthField (sender, '@', 1), 0); ! ! executeCommand (s, "334", @s)}; ! ! bundle { // password ! s = s - "334 "; ! s = base64.decode (s); ! s = string.lower (s); ! ! if not (s == "password:") { ! scriptError ("SMTP AUTH LOGIN failed.")}; ! ! s = base64.encode (string (senderPassword), 0); ! ! executeCommand (s, "235")}; ! ! flAuthenticated = true}; ! "PLAIN" { ! local (s); ! ! executeCommand ("AUTH " + smtpAuth, "334", @s); ! ! s = "\x0" + string.nthField (sender, '@', 1) + "\x0" + string (senderPassword); ! s = base64.encode (s, 0); ! ! executeCommand (s, "235"); ! ! flAuthenticated = true}}; ! ! bundle { // specify sender and recipients ! local (s = "MAIL FROM: <" + string.cleanMailAddress (sender) + ">"); ! ! if flAuthenticated { ! s = s + " AUTH=" + sender}; ! ! executeCommand (s, "250"); ! ! local (i); ! ! if sizeOf (recipient) > 0 { ! for i = 1 to string.countFields (recipient, ',') { ! addRecipient (string.nthField (recipient, ',', i))}}; ! ! if sizeOf (cc) > 0 { ! for i = 1 to string.countFields (cc, ',') { ! addRecipient (string.nthField (cc, ',', i))}}; ! ! if sizeOf (bcc) > 0 { ! for i = 1 to string.countFields (bcc, ',') { ! addRecipient (string.nthField (bcc, ',', i))}}}; ! ! bundle { //send message data ! executeCommand ("DATA", "354"); ! sendCommandToServer (stream, buffer); ! executeCommand (".", "250")}; ! ! closeConnection (); ! ! return (true)}; ! ! «bundle // testing ! «sendMail ("fro...@ya...", "tcp.sendMail SMTP AUTH CRAM-MD5", "This is a test of the tcp.sendMail verb using the SMTP AUTH CRAM-MD5 feature.", "yo...@yo...", smtpAuth: "CRAM-MD5", senderPassword: "your.password") ! «sendMail ("fro...@ya...", "tcp.sendMail SMTP AUTH LOGIN", "This is a test of the tcp.sendMail verb using the SMTP AUTH LOGIN feature.", "yo...@yo...", smtpAuth: "LOGIN", senderPassword: "your.password") ! «sendMail ("fro...@ya...", "tcp.sendMail SMTP AUTH PLAIN", "This is a test of the tcp.sendMail verb using the SMTP AUTH PLAIN feature.", "yo...@yo...", smtpAuth: "PLAIN", senderPassword: "your.password") \ No newline at end of file |