html post
[1/42] from: javierd:paralax:mx at: 6-Aug-2002 0:43
Hello.
I am writting a cgi-script so a user could upload some files to my site
from a web page.
I take as start the "CGI Form Dumper" " from the library, altough i have to
patch it a bit so it could work, i decidet to use post, since some files
are big (up to 2 MB..)
My problem is that the uploaded files seems to be corrupted. Text/files
have the CR/LF changed altough the save specifies bynary. And save files
seems to be some of bytes bigger...
there could be a problem decoding the port, or maybe parsing it, but i
can,t find the problem.
another thing is that maybe i am using the wrong aproach...
It could be simples to sue FTP, but for most of my users this sounds like
greek...and asking to instal rebol and a script is too much...
so i would apreciate any sugestion... mmmmh. a im a "weekend programmer.."
so please be patient...
Javier Delgado
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[2/42] from: g:santilli:tiscalinet:it at: 6-Aug-2002 11:10
Hi Javier,
On Tuesday, August 6, 2002, 7:43:07 AM, you wrote:
JD> My problem is that the uploaded files seems to be corrupted. Text/files
JD> have the CR/LF changed altough the save specifies bynary. And save files
JD> seems to be some of bytes bigger...
I assume you are reading your data from the server via
SYSTEM/PORTS/INPUT; this port is, by default, opened as STRING,
not BINARY, and this is the reason for the conversion of line
terminators (and thus corruption of binary files).
A:
set-modes system/ports/input [binary: true]
at the start of your CGI script should be enough to solve your
problem. (Maybe you'll need to switch it back to STRING mode for
outputting data, but it should not be required if you pay
attention to the line terminators you are sending.)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[3/42] from: javierd:paralax:mx at: 6-Aug-2002 21:50
At 11:10 a.m. 06/08/2002 +0200, you wrote:
>Hi Javier,
>On Tuesday, August 6, 2002, 7:43:07 AM, you wrote:
<<quoted lines omitted: 5>>
>not BINARY, and this is the reason for the conversion of line
>terminators (and thus corruption of binary files).
thanks i will try it.
ia am using :
str: make string! input do decode-cgi str
I supose this is what one gets using voodo programing ( not undestanding
the code...)
Javier Delgdo
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[4/42] from: javierd:paralax:mx at: 7-Aug-2002 3:55
At 11:10 a.m. 06/08/2002 +0200, you wrote:
>I assume you are reading your data from the server via
>SYSTEM/PORTS/INPUT; this port is, by default, opened as STRING,
>not BINARY, and this is the reason for the conversion of line
>terminators (and thus corruption of binary files).
>
> set-modes system/ports/input [binary: true]
if a set this, my script does not work, so I asume is not compatible with
cgi-decode?
should i read directly the imput string and parse it?
Javier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[5/42] from: g:santilli:tiscalinet:it at: 7-Aug-2002 12:11
Hi Javier,
On Wednesday, August 7, 2002, 4:50:12 AM, you wrote:
JD> ia am using :
JD> str: make string! input do decode-cgi str
Are you sending the files in text/www-form-urlencoded format? If
you're getting your data as multipart/form-data as I think you
are, DECODE-CGI isn't able to process it AFAIK...
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[6/42] from: g:santilli:tiscalinet:it at: 7-Aug-2002 12:25
Hi Javier,
On Wednesday, August 7, 2002, 10:55:12 AM, you wrote:
JD> should i read directly the imput string and parse it?
Yes, if you are sending files and not just data from the browser,
because DECODE-CGI does not handle them. I think you will find the
code to handle file uploads in the Script Library. (Maybe others
can point you to the right script to look at.)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[7/42] from: javierd:paralax:mx at: 7-Aug-2002 11:42
At 12:11 p.m. 07/08/2002 +0200, you wrote:
>Are you sending the files in text/www-form-urlencoded format? If
>you're getting your data as multipart/form-data as I think you
>are, DECODE-CGI isn't able to process it AFAIK...
mmmh.. i am using:
<form action="/cgi-bin/dump-post.cgi" method="post"
enctype="multipart/form-data" name="upload">
I could not send files with other options....
it seems I need to read more what going on.... :-/
Javier Delgado
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[8/42] from: javierd:paralax:mx at: 7-Aug-2002 11:54
>Yes, if you are sending files and not just data from the browser,
>because DECODE-CGI does not handle them. I think you will find the
>code to handle file uploads in the Script Library. (Maybe others
>can point you to the right script to look at.)
The "easier" code i found in the library use the decode-cgi function... and
requires enctype="multipart/form-data"
So, if i understand the process. i should set the port to binay, and read
the data form the port?
the code i found is:
input-cgi: func [/stdin] [
stdin: make string! 15000
either system/options/cgi/request-method = "POST" [
read-io system/ports/input stdin 15000
return stdin
][
system/options/cgi/query-string
]
]
Is this correct?. should I set the string to the maximum size i will
expect?.. in this case about 2 MB...
now, how is this data formated?, where i can found this info, so i could
parse it..?
I looks interesting...
Javier Delgado
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[9/42] from: al:bri:xtra at: 8-Aug-2002 7:37
> input-cgi: func [/stdin] [
> stdin: make string! 15000
> either system/options/cgi/request-method = "POST" [
> read-io system/ports/input stdin 15000
An important point I rediscovered in writing my Rebol/Wiki script, is to
make sure that the number in the call to read-io is one or two _*less*_ than
the allocated size! In other words, like:
stdin: make string! 15000 + 2 ; Required spare space for read-io!
either system/options/cgi/request-method = "POST" [
read-io system/ports/input stdin 15000
The relevant code from my Rebol/Wiki is:
if Post? [
use [Length] [ ; Extra length for use by Rebol's 'read-io.
Query: make string! 2 + Length: to-integer
Rebol/options/cgi/content-length
read-io Rebol/ports/input Query Length
Query
]
]
And for writing binary data, from my Rebol/Wiki:
Mime-Data: func [Mime [path!] Data [binary!]] [
print ["Content-Type:" :Mime newline]
write-io system/ports/output Data length? Data
quit
]
and used like:
Mime-Data 'application/pdf read/binary %MyAdobe.pdf
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[10/42] from: al:bri:xtra at: 8-Aug-2002 7:47
Javier, you might be interested in looking at my Rebol/Wiki. It's a CGI
script, that reads POST-ed form data, as well as GET urls. It's attached.
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
-><-
----- Original Message -----
From: "Javier Delgado" <[javierd--paralax--com--mx]>
To: <[rebol-list--rebol--com]>
Sent: Thursday, August 08, 2002 4:42 AM
Subject: [REBOL] Re: html post
> At 12:11 p.m. 07/08/2002 +0200, you wrote:
> >Are you sending the files in text/www-form-urlencoded format? If
<<quoted lines omitted: 9>>
> www.paralax.com.mx
> Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d,
anaglifos
> tel 5373 3620 y 5363 4953
> Naucalpan de Juarez, Edo. Mex.
<<quoted lines omitted: 5>>
> [rebol-request--rebol--com] with "unsubscribe" in the
> subject, without the quotes.
-- Attached file included as plaintext by Listar --
-- File: Wiki.r
#! C:\Rebol\View\rebol.exe -cs
Rebol [
Name: 'Wiki
Title: "Wiki"
File: %Wiki.r
Author: "Andrew Martin"
eMail: [Al--Bri--xtra--co--nz]
Web: http://valley.150m.com
Date: 6/August/2002
Version: 1.3.3
Purpose: {Wiki. Creates a Wiki using Rebol and the Xitami web server.}
Category: [util net markup 5]
Comments: {
In %/Xitami/xitami.cfg -- the Xitami configuration file,
in the [Mime] section, add:
htc=text/plain
to allow the %Calender.htc hypertext component to be sent.
Caution: Hypertext components only work with MSIE!
Also, change:
js=application/x-javascript
to:
js=text/javascript
so as to correctly set the MIME for JavaScript files.
}
Defects: {
Need to fix up dates.
Decide on Active Rebol Content:
- Parrallel to Wiki?
- Inside Wiki?
- Part of Wiki?
- Something else?
}
]
if not value? 'Values [
do %/C/Rebol/Values/Values.r
]
Remote: %/C/Xitami/cgi-bin/ ; Change to suit Colenso IIS!
Wiki_Folder: %/C/Rebol/Wiki/Files/ ; Where all the Wiki's .txt files are stored.
Stylesheet: %Wiki.css
if none? system/options/cgi/script-name [
foreach File read %. [
if found? find [
%.r
%.jpg %.png %.gif
%.css
%.htc
%.js
] extension? File [
write/binary Remote/:File read/binary File
]
]
]
if none? Rebol/options/cgi/script-name [
browse http://localhost/cgi-bin/Wiki.r?
quit
]
See-Other: func [URL [url!]] [
print rejoin [
Rebol/options/cgi/server-protocol " Status: 303 See Other" newline
"Location: " URL newline
]
quit
]
Envelope: func [Title [string!] Body [block!]] [
content-type 'text/html
print newline
print ML compose/deep [
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
html [
head [
title (Title)
link/rel/type/href "stylesheet" "text/css" (Stylesheet)
script/type/language/src "text/javascript" "JavaScript" %Wiki.js ""
]
body/onload/id "SetColumnHeight (HtmlElement)" "HtmlElement" [(Body)]
]
]
quit
]
Log: func [Value] [
write/append %Log.txt reform [now/time mold Value newline]
Value
]
Inspect: func [Value] [
Envelope "Inspect" compose/deep [
div/id "Center_Column" [
h1 "Inspect"
p ["As of: " (now/time) ", the value is: "]
pre [(mold Value)]
]
]
]
Mime-Data: func [Mime [path!] Data [binary!]] [
print ["Content-Type:" :Mime newline]
write-io system/ports/output Data length? Data
quit
]
Forbidden: {:*?"<>|/\.} ; A Wiki file name cannot contain any of these characters.
Permitted: complement charset Forbidden
Magic: #"*"
Wiki: make object! [
Name: [some Permitted]
set 'Wiki_Filename_Rule Filename: [Name opt [#"." Name]]
Relative_Folder: [some [Name #"/"]]
Absolute_Folder: [#"/" opt Relative_Folder]
set 'Wiki_Folder_Rule Folder: [Absolute_Folder | Relative_Folder]
set 'Wiki_File_Rule File: [opt Folder Filename | Folder]
Files: sort recursive-read Wiki_Folder
Rebols: make block! 10
use [Folder File] [
forall Files [
if all [
(%.r = extension? File: first Files)
Folder: pick Files 2
(#"/" = last Folder)
(= filename? File filename? Folder)
] [
remove/part Files 2
]
]
Files: head Files
]
Folders: make block! length? Files
foreach File Files [
if #"/" = last File [
append Folders File
]
]
sort/compare Folders func [File1 [file!] File2 [file!]] [> length? File1 length? File2]
]
Separator: charset "/-"
Month_Rule: [1 2 digit | 3 12 alpha]
Date_Rule: [
[1 2 digit Separator Month_Rule Separator [4 digit | 2 digit]]
| [4 digit Separator Month_Rule Separator 1 2 digit]
]
Deplus: func [String [string!]] [
replace/all dehex replace/all copy String #"+" #" " CRLF newline
]
CGI: make object! [
Post?: "POST" = Rebol/options/cgi/request-method
Get?: "GET" = Rebol/options/cgi/request-method
Script-File: to-file Rebol/options/cgi/script-name
Script-URL: join make url! compose [
http (join Rebol/options/cgi/server-name Rebol/options/cgi/script-name)
] #"?"
Query: Rebol/options/cgi/query-string
SubFolder_Rule: #" " ; A space will never arrive from browser as it's encoded to " ".
if not empty? Wiki/Folders [
SubFolder_Rule: make block! 2 * length? Wiki/Folders
foreach Folder Wiki/Folders [
repend SubFolder_Rule [
replace/all form Folder #" " " " '|
]
]
remove back tail SubFolder_Rule
]
Tuple_Rule: [
some [some Permitted #"|"]
]
Decode: function [Args [any-string! none!]] [Block Folder Name Value Error] [
Block: make block! 10
either all [
string? Args
not empty? Args
parse/all Args [
[
some [
[[Magic | "%2A"] copy Name [Alpha some [AlphaDigit | #"-" | #"_"]]]
[
(Value: none)
#"="
[[copy Value to #"&" skip] | [copy Value to end]]
] (
append Block compose [
(to-set-word Name) (all [Value Deplus Value])
]
)
]
end
]
| [
opt [
copy Folder [opt #"/" SubFolder_Rule] (
Folder: to-file deplus Folder
)
]
opt [
copy Value Date_Rule (
Error: error? try [
Name: 'Date
Value: load Value
]
)
| copy Value Tuple_Rule (
Name: 'Tuple
)
| copy Value Wiki_File_Rule (
Name: 'File
Value: to-file deplus Value
)
]
opt [#"?" (Name: 'Search)]
end (
if all [
none? Value
Folder
] [
Value: Folder
Folder: none
]
if none? Name [
Name: 'File
]
if Folder [
append Block compose [
Folder: (Folder)
]
]
append Block compose [
(to-set-word Name) (Value)
]
)
]
]
not Error
not empty? Block
] [
make object! Block
] [
none
]
]
Object: Decode any [
if Get? [Query]
if Post? [
use [Length] [ ; Extra length for use by Rebol's 'read-io.
Query: make string! 2 + Length: to-integer Rebol/options/cgi/content-length
read-io Rebol/ports/input Query Length
Query
]
]
]
all [
string? Query
Query: Deplus Query
]
]
Folder_Buttons: function [File [file! none!]] [Folders Stack Folder] [
if none? File [
return ""
]
if all [
not empty? File
#"/" != first File
] [
File: head insert File #"/"
]
compose/deep [
div/id "Left_Column" [
form [
(
Folders: parse/all File "/"
if all [
1 = length? Folders
empty? Folders/1
] [
clear Folders
]
Stack: make block! 6 * length? Folders
for Index 1 -1 + length? Folders 1 [
Folder: Folders/:Index
push Stack compose/deep [
input/type/value/onclick/title/class "button" (join Folder #"/") (
rejoin [
"window.location='"
CGI/Script-URL
copy/part File find/tail File join Folder #"/"
{'}
]
) "Click to change Folder" "Folders"
br
]
]
Stack
)
]
]
]
]
Command: CGI/Object
if all [
none? Command
string? CGI/Query
not empty? CGI/Query
] [
See-Other CGI/Script-URL
]
if none? Command [
Command: make object! [
File: %/
]
]
if in Command 'Date [
Command: make Command [
File: join to-file Command/Date %.txt
]
]
Random_Page: make object! [
random/seed now
Title: "Random"
Command?: function [Command [object!]] [File] [
if all [
in Command 'Random
Title = Command/Random
not empty? Wiki/Files
] [
File: first random Wiki/Files
See-Other rejoin [CGI/Script-URL File]
]
]
GUI: does [
if not empty? Wiki/Files [
compose/deep [
form/id/name/method/action (Title) (Title) "GET" (CGI/Script-File) [
input/type/name/value "hidden" (join Magic Title) (Title)
input/type/value/title "submit" (Title) "Click for a random page"
]
]
]
]
]
Search: make object! [
Title: "Search"
Command?: function [Command [object!]] [Query Results Text Index Heading] [
if in Command 'Search [
if none? Query: Command/Search [
See-Other CGI/Script-URL
]
Query: to-string Query
Results: make block! 100
append Results [
tr [
th "Results"
th "Document"
]
]
foreach File Wiki/Files [
if %.txt = Extension? File [
Text: read Wiki_Folder/:File
if found? Index: find Text Query [
File: head clear Extension? File
append Results compose/deep [
tr [
td [
(rejoin ["..." copy/part Index -35])
span/class "Hilight" (copy/part Index length? Query)
(append copy/part at Index 1 + length? Query 35 "...")
]
td [
a/href (
rejoin [CGI/Script-File #"?" #"/" File]
) (File)
]
]
]
]
]
]
Envelope Heading: rejoin ["Search: " Query] compose/deep [
(Folder_Buttons %/.)
div/id "Center_Column" [
h1 (Heading)
table [(Results)]
]
div/id "Commands" [
(Random_Page/GUI)
(Search/GUI Query)
]
]
]
]
GUI: func [Default [string!]] [
compose/deep [
form/id/name/method/action (Title) (Title) "GET" (CGI/Script-File) [
label [
(rejoin [Title #" " Rebol/script/header/title ": "])
input/type/name/value/title "text" (join Magic Title) (Default)
"Enter your search phrase here."
]
input/type/value/title "submit" (Title)
"Click button to search for your phrase in every page."
]
]
]
]
Save_Page: make object! [
Title: "Save"
Command?: function [Command [object!]] [Folder Name File] [
if all [
in Command 'Folder
Folder: Command/Folder
in Command 'Name
Name: Command/Name
Name: to-file deplus Name
in Command 'Text
] [
if not file? Folder [
Folder: to-file deplus Folder
]
File: Folder/:Name
any [
Extension? File
File: Extension File %.txt
]
See-Other rejoin [
CGI/Script-URL
either none? Command/Text [
if exists? Wiki_Folder/:File [
delete Wiki_Folder/:File
]
any [Folder? File %/]
] [
make-dir/deep Folder? Wiki_Folder/:File
write Wiki_Folder/:File Command/Text
head clear Extension? File
]
]
]
]
GUI: func [Heading [string!] File [file!] Text [string!]] [
compose/deep [
form/id/name/method/action (Title) (Title) "POST" (CGI/Script-File) [
div/id "Center_Column" [
h1 (Heading)
label [
"Folder: " input/type/name/value/title "text" (join Magic "Folder")
(any [Folder? File %/]) "Folder where File is stored."
]
label [
"Name: " input/type/name/value/title "text" (join Magic "Name")
(form Filename? File) "Name of File."
]
textarea/name/rows/cols/wrap/style (join Magic "Text") 25 80
"virtual" "width:100%;" (Text)
]
div/id "Commands" [
input/type/value/title "submit" "Save" "Saves your changes"
]
]
]
]
]
Edit_Page: make object! [
Title: "Edit"
Command?: function [Command [object!]] [File Folder Heading] [
if all [
in Command 'Edit
File: Command/Edit
File: to-file deplus File
exists? Wiki_Folder/:File
] [
Envelope Heading: rejoin [Title ": " Filename? File] compose/deep [
(Folder_Buttons File)
(Save_Page/GUI Heading File read Wiki_Folder/:File)
]
]
]
GUI: func [File [file!]] [
compose/deep [
form/id/name/method/action (Title) (Title) "GET" (CGI/Script-File) [
input/type/name/value "hidden" (join Magic Title) (File)
input/type/value/title "submit" "Edit" "Click to edit this page"
]
]
]
]
Hunt: function [File [file!]] [Bag Filename Extension] [
Bag: File
if none? Folder? File [
Bag: none
Filename: Filename? File
Extension: Extension? File
foreach WF Wiki/Files [
if all [
Filename = Filename? WF
Extension = Extension? WF
] [
either none? Bag [
Bag: WF
][
either file? Bag [
Bag: reduce [Bag WF]
] [
append Bag WF
]
]
]
]
]
Bag
]
View_Page?: function [Command [object!]] [File Folder Page Title EditText] [
if all [
in Command 'File
File: Command/File
#"/" != last File
] [
all [
in Command 'Folder
Folder: Command/Folder
File: Folder/:File
]
Page: File
any [
Extension? Page
Page: Extension Page %.txt
]
if all [
%.txt = Extension? Page
either Folder? Page [
exists? Wiki_Folder/:Page
] [
File: Hunt Page
if all [
file? File
Folder? File
] [
clear extension? File
See-Other rejoin [CGI/Script-URL #"/" File]
]
Page: File
]
] [
Title: form Filename? either file? Page [Page] [first Page]
Envelope Title compose/deep either file? Page [
[
(Folder_Buttons Page)
div/id "Center_Column" [
(
eText/Wiki/Base read Wiki_Folder/:Page rejoin [
CGI/Script-File #"?"
]
)
]
div/id "Commands" [
(Random_Page/GUI)
(Search/GUI Title)
(Edit_Page/GUI Page)
]
]
] [
[
(Folder_Buttons %/.)
div/id "Center_Column" [
h1 (Title)
p/class "Initial" [
{The document named: "} (Title)
{" is in several places. Please choose one:}
]
ul [
(
Links: make block! 100
foreach Wiki_File Page [
if %.txt = Extension? Wiki_File [
Wiki_File: copy/part Wiki_File
Extension? Wiki_File
]
append Links compose/deep [
li [
a/href (
rejoin [
CGI/Script-File #"?" #"/" Wiki_File
]
) (join #"/" Wiki_File)
]
]
]
Links
)
]
]
div/id "Commands" [
(Random_Page/GUI)
(Search/GUI Title)
]
]
]
]
]
]
New_Page?: function [Command [object!]] [File Folder Title Heading] [
if all [
in Command 'File
File: Command/File
#"/" != last File
] [
all [
in Command 'Folder
Folder: Command/Folder
File: Folder/:File
]
if not exists? Wiki_Folder/:File [
Title: form Filename? File
Envelope Heading: join "New: " Title compose/deep [
(Folder_Buttons File)
(
Save_Page/GUI Heading File rejoin [
Title newline
head insert/dup copy "" #"*" length? Title newline
;either date? Name [weekday Name] [""] ; Fix Me!
]
)
]
]
]
]
Delete_Folder: make object! [
Title: "Delete"
Command?: function [Command [object!]] [Folder] [
if all [
in Command 'Delete
Folder: Command/Delete
Folder: to-file deplus Folder
#"/" = last Folder
exists? Wiki_Folder/:Folder
empty? read Wiki_Folder/:Folder
] [
delete Wiki_Folder/:Folder
See-Other rejoin [CGI/Script-URL any [Folder? Folder ""]]
]
]
GUI: func [Folder [file!]] [
compose/deep [
form/id/name/method/action (Title) (Title) "GET" (CGI/Script-File) [
[11/42] from: brett:codeconscious at: 8-Aug-2002 13:21
Hi Andrew,
As far as I'm aware, READ-IO is meant to be used inside some sort of loop,
ie. it is used multiple times until it exhausts the input. So as it stands,
if I haven't misunderstood your code, your input-cgi function is creating an
arbitrary maximum input capacity.
Also IIRC, READ-IO an attracted an RT warning in the past that it should be
avoided where possible because it is low level. In this case I'm pretty sure
it can't be avoided - but hopefully some will up date my knowledge if I'm
incorrect on this. :^)
Regards,
Brett.
[12/42] from: al:bri:xtra at: 8-Aug-2002 16:42
Hi, Brett. You wrote:
> As far as I'm aware, READ-IO is meant to be used inside some sort of loop,
ie. it is used multiple times until it exhausts the input.
I understood that to be the case as well. Unfortunately, I couldn't get that
to work.
> So as it stands, if I haven't misunderstood your code, your input-cgi
function is creating an arbitrary maximum input capacity.
For the example code, yes. In my Rebol/Wiki, where it works well, I use:
Query: make string! 2 + Length: to-integer
Rebol/options/cgi/content-length
read-io Rebol/ports/input Query Length
and so I know the length needed.
> Also IIRC, READ-IO an attracted an RT warning in the past that it should
be avoided where possible because it is low level. In this case I'm pretty
sure it can't be avoided - but hopefully some will up date my knowledge if
I'm incorrect on this. :^)
Let's hope there's something better soon. It's not very nice script at the
moment.
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[13/42] from: al:bri:xtra at: 8-Aug-2002 16:40
> Javier, you might be interested in looking at my Rebol/Wiki. It's a CGI
script, that reads POST-ed form data, as well as GET urls. It's attached.
And if you want a better formatted copy, just email me for the latest
version.
Andrew Martin
detabbed Rebolutionary...
ICQ: 26227169 http://valley.150m.com/
[14/42] from: javierd:paralax:mx at: 8-Aug-2002 1:12
At 07:47 a.m. 08/08/2002 +1200, you wrote:
>Javier, you might be interested in looking at my Rebol/Wiki. It's a CGI
>script, that reads POST-ed form data, as well as GET urls. It's attached.
hey. thanks I will study it.
probably i will get back sonn with a lot of questions...
Javier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[15/42] from: brett:codeconscious at: 8-Aug-2002 17:39
Javier,
http://www.rebol.org/cgi/index.htmlhas a POST.r which says it handles
multipart/form-data. I haven't used it and I don't know if it works with
current releases of REBOL, but it could be worth a look.
Regards,
Brett.
[16/42] from: javierd:paralax:mx at: 8-Aug-2002 2:46
At 04:40 p.m. 08/08/2002 +1200, you wrote:
>And if you want a better formatted copy, just email me for the latest
>version.
thanks, just, my first question...
when the browser sends the data, the cgi script is executed as sonn as the
data is received. or it has to wait untill all the data is received?.
(i would like to implement son kind of progress meter... is it posible?.
i don,t want my user to click several times the click button qhen sending a
large file....
Javier Delgado
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[17/42] from: brett:codeconscious at: 8-Aug-2002 17:43
Hi Andrew,
> For the example code, yes. In my Rebol/Wiki, where it works well, I use:
>
> Query: make string! 2 + Length: to-integer
> Rebol/options/cgi/content-length
> read-io Rebol/ports/input Query Length
>
> and so I know the length needed.
Again, I seem to recall that there was a problem with relying on
content-length, but if it is working for you then hey who am I to disagree!
:^)
Here is an excerpt from a script of mine that process standard input for the
NoteTab editor. So it is not CGI, but very related. The relevent bit is the
while loop condition that checks for the result of READ-IO which I think is
the number of bytes read. I've no idea how this would react in a CGI
environment, maybe if you have a webserver on your personal machine you
could try it? You could probaby ignore my character manipulations (they are
probably only relevent to my editor usage).
standard-input: function [
"Returns a string that comes from standard input."
; May not be suitable for large input streams.
] [buf-size inp inp-buffer log] [
buf-size: 30000
inp-buffer: make string! buf-size
inp: make string! buf-size
while [
clear inp-buffer
0 < read-io system/ports/input inp-buffer buf-size
] [
replace/all inp-buffer "^M" "" ; Handles CR on windows systems.
append inp inp-buffer
]
inp
if all [
0 < length? inp
equal? last inp to-char 26
] [
remove/part at inp length? inp 1
]
inp
]
Regards,
Brett.
[18/42] from: javierd:paralax:mx at: 8-Aug-2002 3:54
At 05:39 p.m. 08/08/2002 +1000, you wrote:
>Javier,
>
>http://www.rebol.org/cgi/index.htmlhas a POST.r which says it handles
>multipart/form-data. I haven't used it and I don't know if it works with
>current releases of REBOL, but it could be worth a look.
This is the script i took as a model. I has a couple of bugs, but the main
problem is that
it corrupts binary files....
for i have been reading here, i need a very diferent aproach to send
files. :-/
JAvier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[19/42] from: petr:krenzelok:trz:cz at: 8-Aug-2002 11:40
Javier Delgado wrote:
> At 04:40 p.m. 08/08/2002 +1200, you wrote:
>
>> And if you want a better formatted copy, just email me for the latest
>> version.
>
> thanks, just, my first question...
>
> when the browser sends the data, the cgi script is executed as sonn as
> the data is received. or it has to wait untill all the data is received?.
Hallo all :-)
ask fast, before I forget the answer once again :-) Well, you have to
implement loop reading your data on server, if you use POST method ....
> (i would like to implement son kind of progress meter... is it posible?.
Why not to build client View app for such purpose?
[20/42] from: petr:krenzelok:trz:cz at: 8-Aug-2002 12:02
Brett Handley wrote:
>Hi Andrew,
>>For the example code, yes. In my Rebol/Wiki, where it works well, I use:
<<quoted lines omitted: 26>>
> clear inp-buffer
> 0 < read-io system/ports/input inp-buffer buf-size
I am interested in this condition here. We try to do some intensive
Ethernet + tcp testing here with our device. Is that condition correct?
What if your input is not fast enought to feed you with data? It does
not necessarily mean, that you received all data, does it? What about
following?
input-port: open/direct/no-wait system/ports/input
while [wait input-port data: copy input port][append inp data]
I think it meets follwoing conditions conditions:
- is async the same way read-io is, or so I think ...
- 'wait should wait for input, so you will not receive empty string, if
no data available
- 'none is gonna be returned, if port is closed, while on the other hand
we have problem with:
- how to properly leave the loop? Maybe using something like:
end-seq: "some-spec-chars or newline"
while [
wait input-port
data: copy port
all [ data not found? find head inp end-seq]
][ append inp data]
just in case you would want to try alternative aproach using 'copy ....
I just hope I am not all that wrong :-)
Cheers,
-pekr-
[21/42] from: al:bri:xtra at: 8-Aug-2002 21:58
Javier wrote:
> when the browser sends the data, the cgi script is executed as sonn as the
data is received. or it has to wait untill all the data is received?.
In my CGI Rebol/Wiki script, all the POST-ed form data is ready and waiting
the instant the script needs it. That's because the value in
Rebol/options/cgi/content-length is just a plain integer! There's nothing
tricky going on behind the scenery.
> (i would like to implement son kind of progress meter... is it posible?. i
don't want my user to click several times the click button when sending a
large file...
When sending a large file, in my experience, the Rebol CGI script isn't
started until all the file information is sent by the browser and received
by the web server.
I hope that helps!
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[22/42] from: g:santilli:tiscalinet:it at: 8-Aug-2002 12:13
Hi Andrew,
On Thursday, August 8, 2002, 6:42:53 AM, you wrote:
AM> and so I know the length needed.
AFAIK READ-IO is not guaranteed to return the asked length; it
returns UP TO the asked length of data, but does not wait for it
to arrive if it is not there. (Anyone correct me if I don't recall
correctly; I no more used READ-IO after the NO-WAIT mode has been
introduced.)
AM> Let's hope there's something better soon. It's not very nice script at the
AM> moment.
READ-IO is basically the same as COPY on a BINARY/NO-WAIT port,
except for the need to allocate a properly sized buffer. So you
can replace you code with something like:
set-modes system/ports/input [binary: true no-wait: true]
data: copy system/ports/input
while [content-length > length? data] [
wait system/ports/input
insert tail data copy system/ports/input
]
(WARNING: not tested)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[23/42] from: al:bri:xtra at: 8-Aug-2002 22:09
Brett wrote:
> Again, I seem to recall that there was a problem with relying on
content-length, but if it is working for you then hey who am I to disagree!
:^)
It did take a while for me to realise the need for two extra character
spaces. I had a aweful lot of .txt files that terminated with CR (instead of
CRLF), or dropped the last character (that's how I noticed it). But I've
been using the wiki software for weeks now on several computers and two type
of web server and all's well. After all, as I understand it, the web server
first receives all the data from the browser, then invokes the CGI (so
setting content-length), collects the CGI output, adds appropriate headers,
then supplies the browser. Each step is discrete and doesn't overlap as far
as I know.
I think the method you're describing is for occasions where Rebol is reading
the data directly from a network rather than buffered by a web server. For
example, a Rebol Web Server (not CGI script!) that's receiving multi-part
form info from browser over a slow network, perhaps containing a file.
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[24/42] from: g:santilli:tiscalinet:it at: 8-Aug-2002 12:06
Hi Brett,
On Thursday, August 8, 2002, 5:21:02 AM, you wrote:
BH> Also IIRC, READ-IO an attracted an RT warning in the past that it should be
BH> avoided where possible because it is low level. In this case I'm pretty sure
BH> it can't be avoided - but hopefully some will up date my knowledge if I'm
BH> incorrect on this. :^)
Indeed, it was needed when SET-MODES didn't exist. Now that it is
possible to set the input port to binary mode, you can get binary
data with just the use of COPY (COPY/PART actually, unless you set
it to NO-WAIT too; I think it is the best thing to do in the case
of possible big input, so you can get it as fast as possible).
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[25/42] from: al:bri:xtra at: 8-Aug-2002 22:30
Here's a tutorial on CGI, which could be helpful:
http://www.whizkidtech.net/cgi-bin/tutorial
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[26/42] from: g:santilli:tiscalinet:it at: 8-Aug-2002 12:25
Hi Javier,
On Thursday, August 8, 2002, 9:46:24 AM, you wrote:
JD> when the browser sends the data, the cgi script is executed as sonn as the
JD> data is received. or it has to wait untill all the data is received?.
It may depend on the server, but the script should be executed as
soon as the server has discovered it is a CGI (so, as soon as it
has received the full HTTP header and decoded it; for small POSTs,
at this time it has probably received all the data too, but in
your case it's likely that when the script is called not all the
data has arrived, and you'll have to wait for it).
JD> (i would like to implement son kind of progress meter... is it posible?.
Not that it is easy to do in HTML. :-) But you can at least send a
Please wait
message as soon as the script executes. (Dunno if
the browser will wait until it has sent all the data tough before
displaying the result; you'll have to check with your target
browsers.)
(Maybe you can use the so-called server-push technique to do a
progress meter; I'm not an expert of this, but IIRC it's just a
matter of sending the correct content type ---
multipart/something, I don't recall at the moment --- and then
sending one page after the other following the MIME format.)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[27/42] from: brett:codeconscious at: 8-Aug-2002 20:59
I'm glad I ventured in - I'm learning (getting rid of old ideas).
Javier, I've now tried out POST.r and it has a number of problems. I know
that there is another file upload script floating around, but I can put my
hands on it right now. Doesn't matter though the strucuture of the data
should be straightforward (follows MIME spec.).
Thanks Gabriele, Petr for the networking updates.
My playing around required .. :^)
Regards,
Brett.
[28/42] from: gchiu:compkarori at: 9-Aug-2002 0:36
I'm late into this thread, and outta touch being off list
for a while...
But I did write a multipart post script a few months ago
that has been used to stress a web server to see how many
simultaneous binary uploads it would take.
From memory, I created a custom http header that had the
content-type set to multipart/form-data, specified the
boundary value, assembled the payload into the appropriate
mime format, inserted the headers into the http port as
text, used set-modes to change to binary, inserted the
payload as binary, and then switched the port back to
lines: true again.
Ethereal was quite useful in getting it working.
--
Graham Chiu
[29/42] from: brett:codeconscious at: 8-Aug-2002 23:09
Hi Graham,
> But I did write a multipart post script a few months ago
> that has been used to stress a web server to see how many
> simultaneous binary uploads it would take.
That would be a handy script to test the responsiveness of the CGI script
under various conditions like fast/slow/timeout connections. :^)
Regards,
Brett.
[30/42] from: brett:codeconscious at: 8-Aug-2002 23:15
I wrote,
> My playing around required .. :^)
Well I've done my playing and got some promising results - YMMV. What I did
is below the cut-line. It would need far more coding to get a production
quality script.
Regards,
Brett.
---------------------8<--------------------------------
#!e:/program files/rebol/core/rebol -cs
REBOL [
Title: "A sample HTML form decode script"
Comment: {
This script is an example only. There is no error handling,
it is not properly tested and is missing lots of logic.
In other words, do not use for production. Use it only
on a local test server. And don't blame me :)
}
Author: {Brett Handley}
Date: 8-Aug-2002
]
;******************************************************
; A MIME function I dug out of another script.
;******************************************************
; This could possibly be a simpler parse expression - but since
; I already had it lying around here it is.
mime-body-parts?: function [[catch]
"The separate parts of multipart mime content."
content [string! binary!]
boundary [string!]
line-terminator [string!] "The line terminator - either CRLF or
newline."
] [
normal-delimiter close-delimiter
prologue epilogue
msg-part parse-result
message-part-pattern
return-block
] [
; Get the parts
return-block: make block! 2
normal-delimiter: rejoin [line-terminator "--" boundary]
close-delimiter: rejoin [line-terminator "--" boundary "--"]
message-part-pattern: [
line-terminator copy msg-part [to normal-delimiter | to
close-delimiter]
(append return-block msg-part)
]
parse-result: parse/all content [
(epilogue: prologue: none)
[
"--" boundary message-part-pattern |
copy prologue to normal-delimiter
]
some [
close-delimiter copy epilogue to end |
normal-delimiter message-part-pattern
]
end
]
if not parse-result [throw make error! "Error: Assumption failed while
parsing parts."]
RETURN return-block
]
;******************************************************
; Begin the HTML response
;******************************************************
print "Pragma: no-cache"
print "Content-type: text/html^m^j"
print {<META HTTP-EQUIV="Pragma" CONTENT="no-cache">}
print {<META HTTP-EQUIV="Expires" CONTENT="0">}
print "<HTML><BODY><PRE>"
print join "<h3>TEST at " [now "</h3>"]
print mold what-dir
;******************************************************
; Determine content-type
;******************************************************
tmp: parse/all system/options/cgi/content-type ";"
content-type: first tmp
;******************************************************
; Get boundary
;******************************************************
repeat param next tmp [
if find param {boundary=} [
boundary: second parse/all trim/head/tail param "="
break
]
]
;******************************************************
;Get Content - Using Gabriele's example
;******************************************************
content-length: to-integer system/options/cgi/content-length
set-modes system/ports/input [binary: true no-wait: true]
content: copy system/ports/input
while [content-length > length? content] [
print "Wait for content..."
wait system/ports/input
insert tail content copy system/ports/input
]
;******************************************************
; Seperate the parts of the multipart form and
; output a rebol representation of them.
; PARSE-HEADER is already built in to REBOL - whole
; content string ends up in CONTENT field of object.
; Converts content to binary unless it is text. Text is
; typically sent across networks using CRLF lineending,
; so here I replace it with REBOL's newline convention.
; Note that REPLACE might be slow.
;******************************************************
parts: mime-body-parts? content boundary CRLF
repeat part parts [
result: copy []
msg: parse-header none part
either all [
in msg 'content-type
not find/match msg/content-type {text}
found? content
] [msg/content: to-binary msg/content] [
msg/content: replace/all CRLF newline
]
probe msg
]
;******************************************************
; Finish off the output
;******************************************************
print "</PRE></BODY></HTML>"
QUIT
[31/42] from: g:santilli:tiscalinet:it at: 8-Aug-2002 12:34
Hi Andrew,
On Thursday, August 8, 2002, 11:58:16 AM, you wrote:
AM> When sending a large file, in my experience, the Rebol CGI script isn't
AM> started until all the file information is sent by the browser and received
AM> by the web server.
This actually depends on the web server, I think. It is very
reasonable to pass the control to the script as soon as possible,
for the web server not to have to buffer and handle potentially
very big data. Of course, if Javier is sure that his server is
behaving as you describe, he can avoid using a loop; but since it
is nothing this complicated, I think that being safe (and portable
to other servers...) is not a bad idea. :-)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[32/42] from: javierd:paralax:mx at: 8-Aug-2002 10:26
Gabriele.At 12:25 p.m. 08/08/2002 +0200, you wrote:
>data has arrived, and you'll have to wait for it).
>
>JD> (i would like to implement son kind of progress meter... is it posible?.
>
>Not that it is easy to do in HTML. :-) But you can at least send a
>"Please wait" message as soon as the script executes.
I think that will be enough... at leat that will prevent that the user
would send 10 copies of the same file... :-)
Javier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[33/42] from: javierd:paralax:mx at: 8-Aug-2002 10:22
Gabriele. At 12:06 p.m. 08/08/2002 +0200, you wrote:
>Indeed, it was needed when SET-MODES didn't exist. Now that it is
>possible to set the input port to binary mode, you can get binary
>data with just the use of COPY (COPY/PART actually, unless you set
>it to NO-WAIT too; I think it is the best thing to do in the case
>of possible big input, so you can get it as fast as possible).
That is exactly what i am trying to do, i expected files up to 2 mb... i
will try this,
thanks
Javier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[34/42] from: javierd:paralax:mx at: 8-Aug-2002 10:17
At 11:40 a.m. 08/08/2002 +0200, you wrote:
>ask fast, before I forget the answer once again :-) Well, you have to
>implement loop reading your data on server, if you use POST method ....
Thanks for the sugestion i will do it. :-)
>>(i would like to implement son kind of progress meter... is it posible?.
>
>Why not to build client View app for such purpose?
I know that would be better, but i don,t want that the user had to donwload
o install anything... part of the browser...
but i am giving it a serirous thougth....
JAvier Delgado
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[35/42] from: javierd:paralax:mx at: 8-Aug-2002 10:19
Andrew Martin At 09:58 p.m. 08/08/2002 +1200, you wrote:
>When sending a large file, in my experience, the Rebol CGI script isn't
>started until all the file information is sent by the browser and received
>by the web server.
thanks a lot... finally i understand what i am doing :-)
Javier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[36/42] from: javierd:paralax:mx at: 8-Aug-2002 10:32
Brett. At 08:59 p.m. 08/08/2002 +1000, you wrote:
>I'm glad I ventured in - I'm learning (getting rid of old ideas).
>
>Javier, I've now tried out POST.r and it has a number of problems. I know
>that there is another file upload script floating around, but I can put my
>hands on it right now. Doesn't matter though the strucuture of the data
>should be straightforward (follows MIME spec.).
the other script i found is more an example of how to receive the data:
input-cgi: func [/stdin] [
stdin: make string! 15000
either system/options/cgi/request-method = "POST" [
read-io system/ports/input stdin 15000
return stdin
][
system/options/cgi/query-string
]
]
so far thas are the only two examples. There is another to simulate a a
browser sending post data to a server...
JAvier
Paralax Multimedia
www.paralax.com.mx
Interactivos, kioscos, sites, web hosting, video 3d, animacion 3d, anaglifos
tel 5373 3620 y 5363 4953
Naucalpan de Juarez, Edo. Mex.
tambien visite:
http://www.kaliman.com.mx
http://www.paralax.com.mx/javier
[37/42] from: g:santilli:tiscalinet:it at: 8-Aug-2002 15:48
Hi Andrew,
On Thursday, August 8, 2002, 12:09:12 PM, you wrote:
AM> first receives all the data from the browser, then invokes the CGI (so
AM> setting content-length), collects the CGI output, adds appropriate headers,
The Content-Length field is (and must be) supplied by the browser.
AM> I think the method you're describing is for occasions where Rebol is reading
AM> the data directly from a network rather than buffered by a web server. For
What can I say, if I were writing a web server I'd pass the
control to the CGI script as soon as possible.
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[38/42] from: brett:codeconscious at: 9-Aug-2002 9:24
Resending a post (see below) as it looks to have gone lost in the ether.
Brett.
[39/42] from: brett:codeconscious at: 8-Aug-2002 23:17
Argh! I hate those last minute little changes that I think I've tested and
in fact haven't.
Please change the following line
msg/content: replace/all CRLF newline
to
msg/content: replace/all msg/content CRLF newline
Brett.
[40/42] from: atruter:hih:au at: 12-Aug-2002 12:53
> msg/content: replace/all msg/content CRLF newline
Have I missed something here? Isn't:
msg/content: replace/all msg/content CRLF newline
the same as:
replace/all msg/content CRLF newline
On a side note, I find the first form useful to create copies of file names
but with a different extension, as in:
dat-file: replace copy jpg-file %.jpg %.dat
Regards,
Ashley
[41/42] from: brett:codeconscious at: 12-Aug-2002 13:44
I don't think you've missed anything. My only defense is that it must have
been late. :^)
Regards,
Brett.
[42/42] from: anton:lexicon at: 12-Aug-2002 17:32
That could be just fine but fails for
certain filenames, like this:
%afile.jpg.htm
Sometimes you see urls with an embedded url
in them, too, so you could have two filename
extensions in those.
Anton.
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted