1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-27 02:56:18 -04:00

Merge branch 'elinks-0.12' into elinks-0.13

Conflicts:

	src/document/document.h
	src/encoding/encoding.c
	src/network/connection.c
	src/protocol/bittorrent/bittorrent.c
	src/protocol/bittorrent/bittorrent.h
	src/protocol/bittorrent/common.h
	src/protocol/bittorrent/connection.c
	src/protocol/bittorrent/dialogs.c
	src/protocol/bittorrent/tracker.c
	src/protocol/file/cgi.c
	src/protocol/http/http.c
This commit is contained in:
Kalle Olavi Niemitalo 2008-08-03 21:33:48 +03:00 committed by Kalle Olavi Niemitalo
commit a73fe73cd2
72 changed files with 2488 additions and 637 deletions

22
NEWS
View File

@ -54,13 +54,14 @@ have already been considered.
* Fixed bugs that were not in previous versions: * Fixed bugs that were not in previous versions:
- critical bug 1009: assertion failure in add_snippets() - critical bug 1009: assertion failure in add_snippets()
(mostly reverted)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
ELinks 0.12pre1.GIT now: ELinks 0.12pre1.GIT now:
------------------------ ------------------------
To be released as 0.12pre2, 0.12rc1, or even 0.12.0. This branch To be released as 0.12pre2, 0.12rc1, or even 0.12.0. This branch also
generally also includes the bug fixes made in ELinks 0.11.4.GIT. includes the changes listed under "ELinks 0.11.4.GIT now" below.
* bug 954, enhancement 952: Keep track of ECMAScript form and input * bug 954, enhancement 952: Keep track of ECMAScript form and input
objects instead of constructing new ones on every access. When the objects instead of constructing new ones on every access. When the
@ -72,15 +73,24 @@ generally also includes the bug fixes made in ELinks 0.11.4.GIT.
JS_CallFunction, which can crash if given a closure. JS_CallFunction, which can crash if given a closure.
* critical bug 1031: Use the same JSRuntime for both user SMJS and * critical bug 1031: Use the same JSRuntime for both user SMJS and
scripts on web pages, to work around SpiderMonkey bug 378918. scripts on web pages, to work around SpiderMonkey bug 378918.
* bug 698: Attach controls to the intended form even if it is * bug 1013: Don't assume errno values are between 0 and 100000.
incorrectly nested in a table. (Was broken in 0.11.4.) * bug 1022: Add connection.ssl.trusted_ca_file setting for GnuTLS.
Before this, ELinks did not trust any certificate authorities when
it used GnuTLS, so certificate verification always failed if you
enabled it at connection.ssl.cert_verify.
* bug 1040: Blacklist servers that don't support TLS. This reduces
SSL errors especially in HTTP POST requests using GnuTLS.
* minor bug 951: SpiderMonkey scripting objects used to prevent ELinks * minor bug 951: SpiderMonkey scripting objects used to prevent ELinks
from removing files from the memory cache from removing files from the memory cache.
Bugs that should be removed from NEWS before the 0.12.0 release: Bugs that should be removed from NEWS before the 0.12.0 release:
* critical: Fix crash after a tab was opened during reload. This was * critical: Fix crash after a tab was opened during reload. This was
triggered by the bug 620 fix in ELinks 0.12pre1. triggered by the bug 620 fix in ELinks 0.12pre1.
* critical bug 1018: Avoid an assertion failure when selecting a value
from a pop-up menu for an input field in a tab that is no longer
current, e.g. because another tab was opened with elinks -remote.
This bug was first released in ELinks 0.12pre1.
* major bug 1026 in user SMJS: Protect the callback of elinks.load_uri * major bug 1026 in user SMJS: Protect the callback of elinks.load_uri
from the garbage collector. The elinks.load_uri method was added in from the garbage collector. The elinks.load_uri method was added in
ELinks 0.12pre1. ELinks 0.12pre1.
@ -262,6 +272,8 @@ To be released as 0.11.5.
* critical bug 1030: an assertion used to fail in the search dialog * critical bug 1030: an assertion used to fail in the search dialog
on systems that lack a usable <regex.h> on systems that lack a usable <regex.h>
* major bug 503: various fixes in parsing and updating of elinks.conf * major bug 503: various fixes in parsing and updating of elinks.conf
* bug 698: Attach controls to the intended form even if it is
incorrectly nested in a table. (Was broken in 0.11.4.)
* build bug 1021: fixed uninitialized variable in http_got_header * build bug 1021: fixed uninitialized variable in http_got_header
ELinks 0.11.4: ELinks 0.11.4:

View File

@ -191,7 +191,7 @@ AC_CHECK_HEADERS(fcntl.h limits.h time.h unistd.h)
AC_CHECK_HEADERS(sigaction.h) AC_CHECK_HEADERS(sigaction.h)
AC_CHECK_HEADERS(arpa/inet.h) AC_CHECK_HEADERS(arpa/inet.h)
AC_CHECK_HEADERS(netinet/in_systm.h netinet/in_system.h netinet/ip.h) AC_CHECK_HEADERS(netinet/in_systm.h netinet/in_system.h netinet/ip.h)
AC_CHECK_HEADERS(net/if.h netdb.h netinet/in.h netinet/in6_var.h) AC_CHECK_HEADERS(netdb.h netinet/in.h netinet/in6_var.h)
AC_CHECK_HEADERS(ifaddrs.h) AC_CHECK_HEADERS(ifaddrs.h)
AC_CHECK_HEADERS(sys/cygwin.h io.h) AC_CHECK_HEADERS(sys/cygwin.h io.h)
AC_CHECK_HEADERS(sys/fmutex.h) AC_CHECK_HEADERS(sys/fmutex.h)
@ -202,6 +202,11 @@ AC_CHECK_HEADERS(sys/signal.h)
AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(sys/socket.h)
AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/time.h)
AC_CHECK_HEADERS(sys/utsname.h) AC_CHECK_HEADERS(sys/utsname.h)
AC_CHECK_HEADERS([net/if.h], [], [],
[[#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h> /* <net/if.h> on Mac OS X 10.5.4 needs this */
#endif
]])
AC_CHECK_HEADERS(stdint.h inttypes.h) AC_CHECK_HEADERS(stdint.h inttypes.h)
AC_CHECK_HEADERS(locale.h pwd.h) AC_CHECK_HEADERS(locale.h pwd.h)
AC_CHECK_HEADERS(termios.h) AC_CHECK_HEADERS(termios.h)

View File

@ -14,7 +14,9 @@ PDF_DIR = $(DESTDIR)$(docdir)/$(PACKAGE)/pdf
TXT_DIR = $(DESTDIR)$(docdir)/$(PACKAGE)/txt TXT_DIR = $(DESTDIR)$(docdir)/$(PACKAGE)/txt
ASCIIDOC_CONF = $(srcdir)asciidoc.conf ASCIIDOC_CONF = $(srcdir)asciidoc.conf
ASCIIDOC_FLAGS += -f $(ASCIIDOC_CONF) \ ASCIIDOC_FLAGS += --no-conf -f $(srcdir)tools/asciidoc/asciidoc.conf \
-f $(srcdir)tools/asciidoc/$(call backend).conf \
-f $(ASCIIDOC_CONF) \
-a "builddir=$(CURDIR)/" \ -a "builddir=$(CURDIR)/" \
-a asciidoc7compatible \ -a asciidoc7compatible \
-a elinks_version=$(VERSION) -a elinks_version=$(VERSION)

View File

@ -0,0 +1,18 @@
Copyright (C) 2000 Stuart Rackham
Email: srackham@methods.co.nz
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

14
doc/tools/asciidoc/README Normal file
View File

@ -0,0 +1,14 @@
The files in this directory, except this README itself,
have been copied from the AsciiDoc 7.1.2 source package.
In the past, we have hit annoying incompatibilities when
people have attempted to rebuild ELinks documentation
with new versions of AsciiDoc:
http://bugzilla.elinks.cz/show_bug.cgi?id=989
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=491820
So now we bundle the configuration files from a known good version
to let ELinks documentation be built with them regardless of which
version has been installed on the machine. This should keep things
working as long as the configuration file format remains compatible.

View File

@ -0,0 +1,335 @@
#
# asciidoc.conf
#
# Asciidoc global configuration file.
# Contains backend independent configuration settings that are applied to all
# AsciiDoc documents.
#
[miscellaneous]
tabsize=8
textwidth=70
newline=\r\n
[attributes]
iconsdir=./images/icons
encoding=UTF-8
quirks=
empty=
amp=&
lt=<
gt=>
brvbar=|
nbsp=&#160;
backslash=\
# Attribute and AttributeList element patterns.
attributeentry-pattern=^:(?P<attrname>[a-zA-Z].*?):(?P<attrvalue>.*)$
attributelist-pattern=(?u)(^\[\[(?P<id>[\w\-_]+)\]\]$)|(^\[(?P<attrlist>.*)\]$)
[titles]
subs=specialcharacters,quotes,replacements,macros,attributes
# Double-line title pattern and underlines.
sectiontitle=^(?P<title>.*?)$
underlines="==","--","~~","^^","++"
# Single-line title patterns.
sect0=^= (?P<title>[\S].*)$
sect1=^== (?P<title>[\S].*)$
sect2=^=== (?P<title>[\S].*)$
sect3=^==== (?P<title>[\S].*)$
sect4=^===== (?P<title>[\S].*)$
blocktitle=^\.(?P<title>\S.*)$
[specialcharacters]
&=&amp;
<=&lt;
>=&gt;
[quotes]
# Don't use "double-quote" characters -- they interfere with quoted element
# attribute values (see ``alternative'' below).
*=strong
'=emphasis
`=monospaced
``|''=quoted
\##|##=unquoted
[specialwords]
emphasizedwords=
strongwords=
monospacedwords=
[tags]
# $$ inline passthrough.
passthrough=|
[replacements]
# Replacements performed in order of configuration file entry. The first entry
# of each replacement pair performs the (non-escaped) replacement, the second
# strips the backslash from the esaped replacement.
# (C) Copyright (entity reference &copy;)
(?<!\\)\(C\)=&#169;
\\\(C\)=(C)
# (R) registered trade mark (entity reference &reg;
(?<!\\)\(R\)=&#174;
\\\(R\)=(R)
# (TM) Trademark (entity reference &trade;)
(?<!\\)\(TM\)=&#8482;
\\\(TM\)=(TM)
# -- Spaced and unspaced em dashes (entity reference &mdash;)
# But disallow unspaced in man pages because double-dash option name prefixes
# are pervasive.
ifndef::doctype-manpage[]
(^|[^-\\])--($|[^-])=\1&#8212;\2
endif::doctype-manpage[]
ifdef::doctype-manpage[]
(^|\s*[^\S\\])--($|\s+)=\1&#8212;\2
endif::doctype-manpage[]
\\--(?!-)=--
# ... Ellipsis (entity reference &hellip;)
(?<!\\)\.\.\.=&#8230;
\\\.\.\.=...
##
# The following require non-standard embedded fonts in PDF files so are not
# enabled.
# -> right arrow
#-&gt;=&#8594;
# => right double arrow
#=&gt;=&#8658;
# <- left arrow
#&lt;-=&#8592;
# <= left double arrow
#&lt;\==&#8656;
# Paragraphs.
[paradef-default]
delimiter=(?s)(?P<text>\S.*)
template=paragraph
posattrs=style
verse-style=template="verseparagraph"
NOTE-style=template="admonitionparagraph",name="note",caption="Note"
TIP-style=template="admonitionparagraph",name="tip",caption="Tip"
IMPORTANT-style=template="admonitionparagraph",name="important",caption="Important"
WARNING-style=template="admonitionparagraph",name="warning",caption="Warning"
CAUTION-style=template="admonitionparagraph",name="caution",caption="Caution"
[paradef-literal]
delimiter=(?s)(?P<text>\s+.*)
options=listelement
template=literalparagraph
subs=verbatim
[paradef-admonition]
delimiter=(?s)^\s*(?P<style>NOTE|TIP|IMPORTANT|WARNING|CAUTION):\s+(?P<text>.+)
NOTE-style=template="admonitionparagraph",name="note",caption="Note"
TIP-style=template="admonitionparagraph",name="tip",caption="Tip"
IMPORTANT-style=template="admonitionparagraph",name="important",caption="Important"
WARNING-style=template="admonitionparagraph",name="warning",caption="Warning"
CAUTION-style=template="admonitionparagraph",name="caution",caption="Caution"
[macros]
# Inline macros.
# Backslash prefix required for escape processing.
# (?s) re flag for line spanning.
(?su)[\\]?(?P<name>\w(\w|-)*?):(?P<target>\S*?)(\[(?P<attrlist>.*?)\])=
# Anchor: [[[id]]]. Bibliographic anchor.
(?su)[\\]?\[\[\[(?P<attrlist>[\w][\w-]*?)\]\]\]=anchor3
# Anchor: [[id,xreflabel]]
(?su)[\\]?\[\[(?P<attrlist>[\w"].*?)\]\]=anchor2
# Link: <<id,text>>
(?su)[\\]?&lt;&lt;(?P<attrlist>[\w"].*?)&gt;&gt;=xref2
# Index term: ++primary,secondar,tertiary++
#(?su)(?<!\S)[\\]?\+\+(?P<attrlist>.+?)\+\+(?!\+)=indexterm
(?su)(?<!\S)[\\]?\+\+(?P<attrlist>[^+].*?)\+\+(?!\+)=indexterm
# Index term: +primary+
# Follows ++...++ macro otherwise it will match them.
#(?<!\S)[\\]?\+(?P<attrlist>[^\s\+].*?)\+(?!\+)=indexterm2
(?<!\S)[\\]?\+(?P<attrlist>[^\s\+][^+].*?)\+(?!\+)=indexterm2
# Callout
[\\]?&lt;(?P<index>\d+)&gt;=callout
# Block macros.
(?u)^(?P<name>\w(\w|-)*?)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
^'{4,}$=#ruler
^//([^/].*|)$=#comment
# System macros.
# The default macro which is hardwired into asciidoc.
#(?u)^(?P<name>\w(\w|-)*?)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=+
# Delimited blocks.
[blockdef-comment]
delimiter=^/{4,}
options=skip
[comment-blockmacro]
# Outputs nothing.
[blockdef-sidebar]
delimiter=^\*{4,}$
template=sidebarblock
options=sectionbody
[blockdef-list]
delimiter=^--$
template=listblock
options=list
[listblock]
|
[blockdef-passthrough]
delimiter=^\+{4,}$
template=passthroughblock
subs=attributes,macros
[blockdef-listing]
delimiter=^-{4,}$
template=listingblock
subs=verbatim
[blockdef-literal]
delimiter=^\.{4,}$
template=literalblock
subs=verbatim
posattrs=style
verse-style=template="verseblock",subs="normal"
[blockdef-quote]
delimiter=^_{4,}$
template=quoteblock
options=sectionbody
subs=normal
posattrs=attribution,citetitle
[blockdef-example]
delimiter=^\={4,}$
template=exampleblock
options=sectionbody
posattrs=style
NOTE-style=template="admonitionblock",name="note",caption="Note"
TIP-style=template="admonitionblock",name="tip",caption="Tip"
IMPORTANT-style=template="admonitionblock",name="important",caption="Important"
WARNING-style=template="admonitionblock",name="warning",caption="Warning"
CAUTION-style=template="admonitionblock",name="caution",caption="Caution"
# Lists.
[listdef-bulleted]
type=bulleted
delimiter=^\s*- +(?P<text>.+)$
listtag=ilist
itemtag=ilistitem
texttag=ilisttext
[listdef-bulleted2]
type=bulleted
delimiter=^\s*\* +(?P<text>.+)$
listtag=ilist
itemtag=ilistitem
texttag=ilisttext
[listdef-numbered]
type=numbered
delimiter=^\s*(?P<index>\d*)\. +(?P<text>.+)$
listtag=olist
itemtag=olistitem
texttag=olisttext
[listdef-numbered2]
type=numbered
delimiter=^\s*(?P<index>[.a-z])\. +(?P<text>.+)$
listtag=olist2
itemtag=olistitem
texttag=olisttext
[listdef-vlabeled]
type=labeled
delimiter=^\s*(?P<label>[\S].*)::$
listtag=vlist
itemtag=vlistitem
texttag=vlisttext
entrytag=vlistentry
labeltag=vlistterm
[listdef-vlabeled2]
type=labeled
delimiter=^\s*(?P<label>[\S].*);;$
listtag=vlist
itemtag=vlistitem
texttag=vlisttext
entrytag=vlistentry
labeltag=vlistterm
[listdef-hlabeled]
type=labeled
delimiter=^\s*(?P<label>[\S].*)::\s+(?P<text>.+)$
listtag=hlist
itemtag=hlistitem
texttag=hlisttext
entrytag=hlistentry
labeltag=hlistterm
[listdef-hlabeled2]
type=labeled
delimiter=^\s*(?P<label>[\S].*);;\s+(?P<text>.+)$
listtag=hlist
itemtag=hlistitem
texttag=hlisttext
entrytag=hlistentry
labeltag=hlistterm
# Question and Answer list.
[listdef-qanda]
type=labeled
delimiter=^\s*(?P<label>[\S].*)\?\?$
listtag=qlist
itemtag=qlistitem
texttag=qlisttext
entrytag=qlistentry
labeltag=qlistterm
# Bibliography list.
[listdef-bibliography]
type=bulleted
delimiter=^\+ +(?P<text>.+)$
listtag=blist
itemtag=blistitem
texttag=blisttext
# Glossary list.
[listdef-glossary]
type=labeled
delimiter=^(?P<label>[\S].*):-$
listtag=glist
itemtag=glistitem
texttag=glisttext
entrytag=glistentry
labeltag=glistterm
# Callout list.
[listdef-callout]
type=callout
delimiter=^<?(?P<index>\d*)> +(?P<text>.+)$
listtag=colist
itemtag=colistitem
texttag=colisttext
# Tables.
[tabledef-default]
fillchar=-
format=fixed
[tabledef-csv]
fillchar=~
format=csv
[tabledef-dsv]
fillchar=_
format=dsv

View File

@ -0,0 +1,589 @@
#
# docbook.conf
#
# Asciidoc configuration file.
# Default docbook backend.
#
[miscellaneous]
outfilesuffix=.xml
# Printable page width in pts.
pagewidth=380
pageunits=pt
[attributes]
basebackend=docbook
basebackend-docbook=
[replacements]
# Line break markup is dropped (there is no DocBook line break tag).
(?m)^(.*)\s\+$=\1
# Superscripts.
\^(.+?)\^=<superscript>\1</superscript>
# Subscripts.
~(.+?)~=<subscript>\1</subscript>
[ruler-blockmacro]
# Only applies to HTML so don't output anything.
[image-inlinemacro]
<inlinemediaobject>
<imageobject>
<imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
</imageobject>
<textobject><phrase>{1={target}}</phrase></textobject>
</inlinemediaobject>
[image-blockmacro]
<figure{id? id="{id}"}><title>{title}</title>
{title%}<informalfigure{id? id="{id}"}>
<mediaobject>
<imageobject>
<imagedata fileref="{target}"{width? contentwidth="{width}pt"}{height? contentdepth="{height}pt"}/>
</imageobject>
<textobject><phrase>{1={target}}</phrase></textobject>
</mediaobject>
{title#}</figure>
{title%}</informalfigure>
[indexterm-inlinemacro]
# Inline index term.
# Generate separate index entries for primary, secondary and tertiary
# descriptions.
# Primary only.
{2%}<indexterm>
{2%} <primary>{1}</primary>
{2%}</indexterm>
# Primary and secondary.
{2#}{3%}<indexterm>
{2#}{3%} <primary>{1}</primary><secondary>{2}</secondary>
{2#}{3%}</indexterm>
{2#}{3%}<indexterm>
{2#}{3%} <primary>{2}</primary>
{2#}{3%}</indexterm>
# Primary, secondary and tertiary.
{3#}<indexterm>
<primary>{1}</primary><secondary>{2}</secondary><tertiary>{3}</tertiary>
{3#}</indexterm>
{3#}<indexterm>
<primary>{2}</primary><secondary>{3}</secondary>
{3#}</indexterm>
{3#}<indexterm>
<primary>{3}</primary>
{3#}</indexterm>
[indexterm2-inlinemacro]
# Inline index term.
# Single entry index term that is visible in the primary text flow.
<indexterm>
<primary>{1}</primary>
</indexterm>
{1}
[footnote-inlinemacro]
# Inline footnote.
<footnote><simpara>{0}</simpara></footnote>
[callout-inlinemacro]
# Inline callout.
<co id="{coid}"/>
[tags]
# Bulleted, numbered and labeled list tags.
ilist=<itemizedlist{id? id="{id}"}>{title?<title>{title}</title>}|</itemizedlist>
ilistitem=<listitem>|</listitem>
ilisttext=<simpara>|</simpara>
olist=<orderedlist{id? id="{id}"}>{title?<title>{title}</title>}|</orderedlist>
olist2=<orderedlist{id? id="{id}"} numeration="loweralpha">|</orderedlist>
olistitem=<listitem>|</listitem>
olisttext=<simpara>|</simpara>
vlist=<variablelist{id? id="{id}"}>{title?<title>{title}</title>}|</variablelist>
vlistentry=<varlistentry>|</varlistentry>
vlistterm=<term>|</term>
vlisttext=<simpara>|</simpara>
vlistitem=<listitem>|</listitem>
# Horizontal labeled list (implemented with two column table).
# Hardwired column widths to 30%,70% because the current crop of PDF
# generators do not auto calculate column widths.
hlist=<{title?table}{title!informaltable}{id? id="{id}"} tabstyle="{style=hlabeledlist}" pgwide="0" frame="none" colsep="0" rowsep="0">{title?<title>{title}</title>}<tgroup cols="2"><colspec colwidth="{1=3}*"/><colspec colwidth="{2=7}*"/><tbody valign="top">|</tbody></tgroup><{title?/table}{title!/informaltable}>
hlistentry=<row>|</row>
hlisttext=<simpara>|</simpara>
hlistterm=<entry><simpara>|</simpara></entry>
hlistitem=<entry>|</entry>
# Question and Answer list.
qlist=<qandaset{id? id="{id}"}>{title?<title>{title}</title>}|</qandaset>
qlistentry=<qandaentry>|</qandaentry>
qlistterm=<question><simpara>|</simpara></question>
qlistitem=<answer>|</answer>
qlisttext=<simpara>|</simpara>
# Bibliography list.
blist=|
blistitem=<bibliomixed>|</bibliomixed>
blisttext=<bibliomisc>|</bibliomisc>
# Glossary list.
glist=|
glistentry=<glossentry>|</glossentry>
glistterm=<glossterm>|</glossterm>
glistitem=<glossdef>|</glossdef>
glisttext=<simpara>|</simpara>
# Callout list.
colist=<calloutlist{id? id="{id}"}>{title?<title>{title}</title>}|</calloutlist>
colistitem=<callout arearefs="{coids}">|</callout>
colisttext=<simpara>|</simpara>
# Quoted text
emphasis=<emphasis>|</emphasis>
strong=<emphasis role="strong">|</emphasis>
monospaced=<literal>|</literal>
quoted={amp}#8220;|{amp}#8221;
unquoted=|
# $$ inline passthrough.
passthrough=|
# Inline macros
[http-inlinemacro]
<ulink url="{name}:{target}">{0={name}:{target}}</ulink>
[https-inlinemacro]
<ulink url="{name}:{target}">{0={name}:{target}}</ulink>
[ftp-inlinemacro]
<ulink url="{name}:{target}">{0={name}:{target}}</ulink>
[file-inlinemacro]
<ulink url="{name}:{target}">{0={name}:{target}}</ulink>
[mailto-inlinemacro]
<ulink url="{name}:{target}">{0={target}}</ulink>
#<email>{target}</email>
[link-inlinemacro]
<ulink url="{target}">{0={target}}</ulink>
# anchor:id[text]
[anchor-inlinemacro]
<anchor id="{target}" xreflabel="{0=[{target}]}"/>
# [[id,text]]
[anchor2-inlinemacro]
<anchor id="{1}" xreflabel="{2=[{1}]}"/>
# [[[id]]]
[anchor3-inlinemacro]
<anchor id="{1}" xreflabel="[{1}]"/>[{1}]
# xref:id[text]
[xref-inlinemacro]
<link linkend="{target}">{0}</link>
{2%}<xref linkend="{target}"/>
# <<id,text>>
[xref2-inlinemacro]
<link linkend="{1}">{2}</link>
{2%}<xref linkend="{1}"/>
# Special word macros
[emphasizedwords]
<emphasis>{words}</emphasis>
[monospacedwords]
<literal>{words}</literal>
[strongwords]
<emphasis role="strong">{words}</emphasis>
# Paragraph substitution.
[paragraph]
<formalpara{id? id="{id}"}><title>{title}</title><para>
{title%}<simpara{id? id="{id}"}>
|
{title%}</simpara>
{title#}</para></formalpara>
{empty}
[admonitionparagraph]
<{name}{id? id="{id}"}><simpara>|</simpara></{name}>
[literalparagraph]
# The literal block employs the same markup.
template::[literalblock]
[verseparagraph]
template::[verseblock]
# Delimited blocks.
[literalblock]
<example><title>{title}</title>
<literallayout{id? id="{id}"} class="{font=monospaced}">
|
</literallayout>
{title#}</example>
[listingblock]
<example><title>{title}</title>
<screen>
|
</screen>
{title#}</example>
[verseblock]
<formalpara{id? id="{id}"}><title>{title}</title><para>
{title%}<literallayout{id? id="{id}"}>
{title#}<literallayout>
|
</literallayout>
{title#}</para></formalpara>
[sidebarblock]
<sidebar{id? id="{id}"}>
<title>{title}</title>
|
</sidebar>
[passthroughblock]
|
[quoteblock]
# The epigraph element may be more appropriate than blockquote.
<blockquote{id? id="{id}"}>
<title>{title}</title>
<attribution>
{attribution}
<citetitle>{citetitle}</citetitle>
</attribution>
|
</blockquote>
[exampleblock]
<{title?example}{title!informalexample}{id? id="{id}"}>
<title>{title}</title>
|
</{title?example}{title!informalexample}>
[admonitionblock]
<{name}{id? id="{id}"}>
<title>{title}</title>
|
</{name}>
# Tables.
[tabledef-default]
template=table
colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}"/>
bodyrow=<row>|</row>
bodydata=<entry>|</entry>
[table]
<{title?table}{title!informaltable}{id? id="{id}"} pgwide="0"
frame="{frame=topbot}"
{grid%rowsep="0" colsep="0"}
rowsep="{grid@none|cols:0:1}" colsep="{grid@none|rows:0:1}"
>
<title>{title}</title>
<tgroup cols="{cols}">
{colspecs}
{headrows#}<thead>
{headrows}
{headrows#}</thead>
{footrows#}<tfoot>
{footrows}
{footrows#}</tfoot>
<tbody>
{bodyrows}
</tbody>
</tgroup>
</{title?table}{title!informaltable}>
[specialsections]
ifdef::doctype-article[]
^Abstract$=sect-abstract
endif::doctype-article[]
ifdef::doctype-book[]
^Colophon$=sect-colophon
^Dedication$=sect-dedication
^Preface$=sect-preface
endif::doctype-book[]
^Index$=sect-index
^(Bibliography|References)$=sect-bibliography
^Glossary$=sect-glossary
^Appendix [A-Z][:.](?P<title>.*)$=sect-appendix
# Special sections.
[sect-preface]
<preface{id? id="{id}"}>
<title>{title}</title>
|
</preface>
[sect-index]
<index{id? id="{id}"}>
<title>{title}</title>
|
</index>
[sect-bibliography]
<bibliography{id? id="{id}"}>
<title>{title}</title>
|
</bibliography>
[sect-glossary]
<glossary{id? id="{id}"}>
<title>{title}</title>
|
</glossary>
[sect-appendix]
<appendix{id? id="{id}"}>
<title>{title}</title>
|
</appendix>
[header-declarations]
<?xml version="1.0" encoding="{encoding}"?>
<!DOCTYPE {doctype-article?article}{doctype-book?book}{doctype-manpage?refentry} PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
#-------------------------
# article document type
#-------------------------
ifdef::doctype-article[]
[header]
template::[header-declarations]
<article lang="en">
{doctitle#}<articleinfo>
<title>{doctitle}</title>
<date>{date}</date>
{authored#}<author>
<firstname>{firstname}</firstname>
<othername>{middlename}</othername>
<surname>{lastname}</surname>
<affiliation><address><email>{email}</email></address></affiliation>
{authored#}</author>
<authorinitials>{authorinitials}</authorinitials>
# If file named like source document with -revhistory.xml suffix exists
# include it as the document history, otherwise use current revision.
{revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
{revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{authorinitials?<authorinitials>{authorinitials}</authorinitials>}{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
<corpname>{companyname}</corpname>
{doctitle#}</articleinfo>
[footer]
</article>
[preamble]
# Untitled elements between header and first section title.
|
[sect-abstract]
<abstract{id? id="{id}"}>
|
</abstract>
[sect1]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
[sect2]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
[sect3]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
[sect4]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
endif::doctype-article[]
#-------------------------
# manpage document type
#-------------------------
ifdef::doctype-manpage[]
[replacements]
# The roff format does not substitute special characters so just print them as
# text.
\(C\)=(C)
\(TM\)=(TM)
[header]
template::[header-declarations]
<refentry>
<refmeta>
<refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum>
</refmeta>
<refnamediv>
<refname>{manname}</refname>
<refpurpose>{manpurpose}</refpurpose>
</refnamediv>
[footer]
</refentry>
# Section macros
[sect-synopsis]
<refsynopsisdiv{id? id="{id}"}>
|
</refsynopsisdiv>
[sect1]
<refsect1{id? id="{id}"}>
<title>{title}</title>
|
</refsect1>
[sect2]
<refsect2{id? id="{id}"}>
<title>{title}</title>
|
</refsect2>
[sect3]
<refsect3{id? id="{id}"}>
<title>{title}</title>
|
</refsect3>
endif::doctype-manpage[]
#-------------------------
# book document type
#-------------------------
ifdef::doctype-book[]
[header]
template::[header-declarations]
<book lang="en">
{doctitle#}<bookinfo>
<title>{doctitle}</title>
<date>{date}</date>
{authored#}<author>
<firstname>{firstname}</firstname>
<othername>{middlename}</othername>
<surname>{lastname}</surname>
<affiliation><address><email>{email}</email></address></affiliation>
{authored#}</author>
<authorinitials>{authorinitials}</authorinitials>
# If file named like source document with -revhistory.xml suffix exists
# include it as the document history, otherwise use current revision.
{revisionhistory#}{include:{docdir}/{docname}-revhistory.xml}
{revisionhistory%}<revhistory><revision><revnumber>{revision}</revnumber><date>{date}</date>{authorinitials?<authorinitials>{authorinitials}</authorinitials>}{revremark?<revremark>{revremark}</revremark>}</revision></revhistory>
<corpname>{companyname}</corpname>
{doctitle#}</bookinfo>
[footer]
</book>
[preamble]
# Preamble is not allowed in DocBook book so wrap it in a preface.
<preface{id? id="{id}"}>
<title>Preface</title>
|
</preface>
[sect-dedication]
<dedication{id? id="{id}"}>
|
</dedication>
[sect-colophon]
<colophon{id? id="{id}"}>
|
</colophon>
[sect0]
<part{id? id="{id}"}>
<title>{title}</title>
|
</part>
[sect1]
<chapter{id? id="{id}"}>
<title>{title}</title>
|
</chapter>
[sect2]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
[sect3]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
[sect4]
<section{id? id="{id}"}>
<title>{title}</title>
|
</section>
endif::doctype-book[]
ifdef::sgml[]
#
# Optional DocBook SGML.
#
# Most of the differences between DocBook XML and DocBook SGML boils
# down to the empty element syntax: SGML does not like the XML empty
# element <.../> syntax, use <...> instead.
#
[miscellaneous]
outfilesuffix=.sgml
[header-declarations]
<!DOCTYPE {doctype-article?article}{doctype-book?book}{doctype-manpage?refentry} PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
[tabledef-default]
colspec=<colspec colwidth="{colwidth}{pageunits}" align="{colalign}">
[image-inlinemacro]
<inlinemediaobject>
<imageobject>
<imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
</imageobject>
<textobject><phrase>{1={target}}</phrase></textobject>
</inlinemediaobject>
[image-blockmacro]
<figure><title>{title}</title>
{title%}<informalfigure>
<mediaobject>
<imageobject>
<imagedata fileref="{target}"{width? width="{width}pt"}{height? depth="{height}pt"}>
</imageobject>
<textobject><phrase>{1={target}}</phrase></textobject>
</mediaobject>
{title#}</figure>
{title%}</informalfigure>
# Inline macros
[xref-inlinemacro]
<link linkend="{target}">{0}</link>
{2%}<xref linkend="{target}">
[xref2-inlinemacro]
# <<id,text>>
<link linkend="{1}">{2}</link>
{2%}<xref linkend="{1}">
[anchor-inlinemacro]
<anchor id="{target}" xreflabel="{0=[{target}]}">
[anchor2-inlinemacro]
# [[id,text]]
<anchor id="{1}" xreflabel="{2=[{1}]}">
endif::sgml[]

View File

@ -0,0 +1,28 @@
/* Workarounds for IE6's broken and incomplete CSS2. */
div.sidebar-content {
background: #ffffee;
border: 1px solid silver;
padding: 0.5em;
}
div.sidebar-title, div.image-title {
font-family: sans-serif;
font-weight: bold;
margin-top: 0.0em;
margin-bottom: 0.5em;
}
div.listingblock div.content {
border: 1px solid silver;
background: #f4f4f4;
padding: 0.5em;
}
div.quoteblock-content {
padding-left: 2.0em;
}
div.exampleblock-content {
border-left: 2px solid silver;
padding-left: 0.5em;
}

View File

@ -0,0 +1,229 @@
/* Debug borders */
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
/*
border: 1px solid red;
*/
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
}
strong {
font-weight: bold;
}
tt {
color: navy;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
font-family: sans-serif;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1 {
border-bottom: 2px solid silver;
}
h2 {
border-bottom: 2px solid silver;
padding-top: 0.5em;
}
div.sectionbody {
font-family: serif;
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
pre {
padding: 0;
margin: 0;
}
span#author {
color: #527bbd;
font-family: sans-serif;
font-weight: bold;
font-size: 1.2em;
}
span#email {
}
span#revision {
font-family: sans-serif;
}
div#footer {
font-family: sans-serif;
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
div#footer-text {
float: left;
padding-bottom: 0.5em;
}
div#footer-badges {
float: right;
padding-bottom: 0.5em;
}
div#preamble,
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-right: 10%;
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.5em;
margin-bottom: 2.5em;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
font-family: sans-serif;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid silver;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid silver;
background: #f4f4f4;
padding: 0.5em;
}
div.quoteblock > div.content {
padding-left: 2.0em;
}
div.attribution {
text-align: right;
}
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 2px solid silver;
}
div.exampleblock > div.content {
border-left: 2px solid silver;
padding: 0.5em;
}
div.verseblock div.content {
white-space: pre;
}
div.imageblock div.content { padding-left: 0; }
div.imageblock img { border: 1px solid silver; }
span.image img { border-style: none; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: italic;
}
dd > *:first-child {
margin-top: 0;
}
ul, ol {
list-style-position: outside;
}
ol.olist2 {
list-style-type: lower-alpha;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead {
font-family: sans-serif;
font-weight: bold;
}
tfoot {
font-weight: bold;
}
div.hlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
td.hlist1 {
vertical-align: top;
font-style: italic;
padding-right: 0.8em;
}
td.hlist2 {
vertical-align: top;
}
@media print {
div#footer-badges { display: none; }
}

View File

@ -0,0 +1,43 @@
#
# xhtml11-quirks.conf
#
# Workarounds for IE6's broken # and incomplete CSS2.
#
[image-blockmacro]
<div class="imageblock">
<a id="{id}"></a>
<div class="content">
<a class="image" href="{link}">
<img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"}/>
{link#}</a>
</div>
<div class="image-title">{caption=Figure: }{title}</div>
</div>
[sidebarblock]
<div class="sidebarblock">
<a id="{id}"></a>
<div class="sidebar-content">
<div class="sidebar-title">{title}</div>
|
</div></div>
[quoteblock]
<div class="quoteblock">
<a id="{id}"></a>
<div class="title">{title}</div>
<div class="quoteblock-content">
|
<div class="attribution">
<span class="emphasis">{citetitle}</span><br />
&#8212; {attribution}
</div></div></div>
[exampleblock]
<div class="exampleblock">
<a id="{id}"></a>
<div class="title">{caption=Example: }{title}</div>
<div class="exampleblock-content">
|
</div></div>

View File

@ -0,0 +1,426 @@
#
# xhtml11.conf
#
# Asciidoc configuration file.
# xhtml11 backend, generates XHTML 1.1 conformant markup.
#
[miscellaneous]
outfilesuffix=.html
# Screen width in pixels.
pagewidth=800
pageunits=
[attributes]
basebackend=html
basebackend-html=
[replacements]
# Line break.
(?m)^(.*)\s\+$=\1<br />
# Superscripts.
\^(.+?)\^=<sup>\1</sup>
# Subscripts.
~(.+?)~=<sub>\1</sub>
# Escape ASCIIMathML delimiters.
ifdef::asciimath[]
\$=\$
`=\`
endif::asciimath[]
[ruler-blockmacro]
<hr />
[image-inlinemacro]
<span class="image">
<a class="image" href="{link}">
<img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"}/>
{link#}</a>
</span>
[image-blockmacro]
<div class="imageblock">
<a id="{id}"></a>
<div class="content">
<a class="image" href="{link}">
<img src="{target}" alt="{1={target}}"{1? title="{1}"}{width? width="{width}"}{height? height="{height}"}/>
{link#}</a>
</div>
<div class="title">{caption=Figure: }{title}</div>
</div>
[indexterm-inlinemacro]
# Inline index term.
{empty}
[indexterm2-inlinemacro]
# Inline index term.
# Single entry index term that is visible in the primary text flow.
{1}
[footnote-inlinemacro]
# Inline footnote.
<br />[{0}]<br />
[callout-inlinemacro]
# Inline callout.
<b>({index})</b>
[tags]
# Bulleted, numbered and labeled list tags.
ilist={id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<ul>|</ul>
ilistitem=<li>|</li>
ilisttext=<p>|</p>
olist={id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<ol>|</ol>
olist2={id?<a id="{id}"></a>}<ol class="olist2">|</ol>
olistitem=<li>|</li>
olisttext=<p>|</p>
vlist={id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<dl>|</dl>
vlistentry=|
vlistterm=<dt>|</dt>
vlistitem=<dd>|</dd>
vlisttext=<p>|</p>
# Horizontal labeled list.
hlist=<div class="hlist">{id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<table>{1?<col width="{1}%" />}{2?<col width="{2}%" />}|</table></div>
hlistentry=<tr>|</tr>
hlistterm=<td class="hlist1">|</td>
hlistitem=<td class="hlist2">|</td>
hlisttext=|
# Question and Answer list.
qlist={id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<ol>|</ol>
qlistentry=<li>|</li>
qlistterm=<p><strong>|</strong></p>
qlistitem=|
qlisttext=<p>|</p>
# Callout list.
colist={id?<a id="{id}"></a>}{title?<div class="title">{title}</div>}<ol>|</ol>
colistitem=<li>|</li>
colisttext=<p>|</p>
# Quoted text.
emphasis=<em{0? style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?"}>|</em>
strong=<strong{0? style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?"}>|</strong>
monospaced=<tt{0? style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?"}>|</tt>
quoted={0?<span style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?">}{amp}#8220;|{amp}#8221;{0?</span>}
unquoted={0?<span style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?">}|{0?</span>}
# $$ inline passthrough.
$$passthrough=<span{0? style="}{1?color: {1};}{2?background-color: {2};}{3?font-size: {3}em;}{0?"}>|</span>
# Inline macros
[http-inlinemacro]
<a href="{name}:{target}">{0={name}:{target}}</a>
[https-inlinemacro]
<a href="{name}:{target}">{0={name}:{target}}</a>
[ftp-inlinemacro]
<a href="{name}:{target}">{0={name}:{target}}</a>
[file-inlinemacro]
<a href="{name}:{target}">{0={name}:{target}}</a>
[mailto-inlinemacro]
<a href="{name}:{target}">{0={target}}</a>
[link-inlinemacro]
<a href="{target}">{0={target}}</a>
# anchor:id[text]
[anchor-inlinemacro]
<a id="{target}"></a>
# [[id,text]]
[anchor2-inlinemacro]
<a id="{1}"></a>
# [[[id]]]
[anchor3-inlinemacro]
<a id="{1}">[{1}]</a>
# xref:id[text]
[xref-inlinemacro]
<a href="#{target}">{0=[{target}]}</a>
# <<id,text>>
[xref2-inlinemacro]
<a href="#{1}">{2=[{1}]}</a>
# Special word substitution.
[emphasizedwords]
<em>{words}</em>
[monospacedwords]
<tt>{words}</tt>
[strongwords]
<strong>{words}</strong>
# Paragraph substitution.
[paragraph]
<div class="title">{title}</div>
<p>{id?<a id="{id}"></a>}
|
</p>
[literalparagraph]
# The literal block employs the same markup.
template::[literalblock]
[verseparagraph]
# The verse block employs the same markup.
template::[verseblock]
[admonitionparagraph]
# The admonition block employs the same markup.
template::[admonitionblock]
# Delimited blocks.
[passthroughblock]
|
[listingblock]
<div class="listingblock">
<a id="{id}"></a>
<div class="title">{caption=Example: }{title}</div>
<div class="content">
<pre><tt>
|
</tt></pre>
</div></div>
[literalblock]
<div class="literalblock">
<a id="{id}"></a>
<div class="title">{title}</div>
<div class="content">
{style#}<pre class="{style}"><span>
{style%}<pre><tt>
|
</tt></pre>
</div></div>
[verseblock]
<div class="verseblock">
<a id="{id}"></a>
<div class="title">{title}</div>
<div class="content">
|
</div></div>
[sidebarblock]
<div class="sidebarblock">
<a id="{id}"></a>
<div class="content">
<div class="title">{title}</div>
|
</div></div>
[quoteblock]
<div class="quoteblock">
<a id="{id}"></a>
<div class="title">{title}</div>
<div class="content">
|
<div class="attribution">
<em>{citetitle}</em><br />
&#8212; {attribution}
</div></div></div>
[exampleblock]
<div class="exampleblock">
<a id="{id}"></a>
<div class="title">{caption=Example: }{title}</div>
<div class="content">
|
</div></div>
[admonitionblock]
<div class="admonitionblock">
<a id="{id}"></a>
<table><tr>
<td class="icon">
{icons#}<img src="{icon={iconsdir}/{name}.png}" alt="{caption}" />
{icons%}<div class="title">{caption}</div>
</td>
<td class="content">
<div class="title">{title}</div>
|
</td>
</tr></table>
</div>
# Bibliography list.
# Same as numbered list.
[listdef-bibliography]
listtag=olist
itemtag=olistitem
texttag=olisttext
# Glossary list.
# Same as labeled list.
[listdef-glossary]
listtag=vlist
itemtag=vlistitem
texttag=vlisttext
entrytag=vlistentry
labeltag=vlistterm
# Tables.
[tabledef-default]
template=table
colspec=<col width="{colwidth}{pageunits}" />
bodyrow=<tr>|</tr>
headdata=<th align="{colalign}">|</th>
footdata=<td align="{colalign}">|</td>
bodydata=<td align="{colalign}">|</td>
[table]
<div class="tableblock">
<a id="{id}"></a>
<table rules="{grid=none}"
frame="{frame%hsides}"
frame="{frame@topbot:hsides}{frame@all:border}{frame@none:void}{frame@sides:vsides}"
cellspacing="0" cellpadding="4">
<caption class="title">{caption=Table: }{title}</caption>
{colspecs}
{headrows#}<thead>
{headrows}
{headrows#}</thead>
{footrows#}<tfoot>
{footrows}
{footrows#}</tfoot>
<tbody valign="top">
{bodyrows}
</tbody>
</table>
</div>
[preamble]
# Untitled elements between header and first section title.
<div id="preamble">
<a id="{id}"></a>
<div class="sectionbody">
|
</div>
</div>
# Document sections.
[sect0]
<h1>{id?<a id="{id}"></a>}{title}</h1>
|
[sect1]
<h2>{id?<a id="{id}"></a>}{numbered?{sectnum} }{title}</h2>
<div class="sectionbody">
|
</div>
[sect2]
<h3>{id?<a id="{id}"></a>}{numbered?{sectnum} }{title}</h3>
|
[sect3]
<h4>{id?<a id="{id}"></a>}{numbered?{sectnum} }{title}</h4>
|
[sect4]
<h5>{id?<a id="{id}"></a>}{title}</h5>
|
[header]
# IE6 enters quirks mode if the following XML directive is present.
#<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
<meta name="generator" content="AsciiDoc {asciidoc-version}" />
ifdef::linkcss[]
<link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}.css" type="text/css" />
{doctype-manpage}<link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}-manpage.css" type="text/css" />
ifdef::quirks[]
<link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}-quirks.css" type="text/css" />
endif::quirks[]
endif::linkcss[]
ifndef::linkcss[]
<style type="text/css">
include1::{stylesdir=./stylesheets}/{theme={backend}}.css[]
{doctype-manpage}include1::{stylesdir=./stylesheets}/{theme={backend}}-manpage.css[]
ifdef::quirks[]
include1::{stylesdir=./stylesheets}/{theme={backend}}-quirks.css[]
endif::quirks[]
</style>
endif::linkcss[]
ifdef::asciimath[]
ifdef::linkcss[]
<script type="text/javascript" src="{scriptsdir=.}/ASCIIMathML.js"></script>
endif::linkcss[]
ifndef::linkcss[]
<script type="text/javascript">
# Escape as CDATA to pass validators.
/*<![CDATA[*/
include1::{scriptsdir=./javascripts}/ASCIIMathML.js[]
/*]]>*/
</script>
endif::linkcss[]
endif::asciimath[]
<title>{doctitle}</title>
</head>
<body>
# Article, book header.
ifndef::doctype-manpage[]
<div id="header">
<h1>{doctitle}</h1>
<span id="author">{author}</span><br />
<span id="email"><tt>&lt;<a href="mailto:{email}">{email}</a>&gt;</tt></span><br />
<span id="revision">version {revision}{date?,}</span>
{date}
</div>
endif::doctype-manpage[]
# Man page header.
ifdef::doctype-manpage[]
<div id="header">
<h1>
{doctitle} Manual Page
</h1>
<h2>NAME</h2>
<div class="sectionbody">
<p>{manname} -
{manpurpose}
</p>
</div>
</div>
endif::doctype-manpage[]
[footer]
<div id="footer">
<div id="footer-text">
Version {revision}<br />
Last updated {localdate} {localtime}
</div>
ifdef::badges[]
<div id="footer-badges">
ifndef::icons[]
Valid <a href="http://validator.w3.org/check?uri=referer">XHTML</a>
and <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>.
endif::icons[]
ifdef::icons[]
<a href="http://validator.w3.org/check?uri=referer">
<img style="border:none; width:88px; height:31px;"
src="http://www.w3.org/Icons/valid-xhtml11"
alt="Valid XHTML 1.1!" />
</a>
<a href="http://jigsaw.w3.org/css-validator/check/referer">
<img style="border:none; width:88px; height:31px;"
src="http://jigsaw.w3.org/css-validator/images/vcss"
alt="Valid CSS!" />
</a>
<a href="http://www.mozilla.org/products/firefox/">
<img style="border:none; width:110px; height:32px;"
src="http://www.spreadfirefox.com/community/images/affiliates/Buttons/110x32/safer.gif"
alt="Get Firefox!" />
</a>
endif::icons[]
</div>
endif::badges[]
</div>
</body>
</html>
ifdef::doctype-manpage[]
[sect-synopsis]
template::[sect1]
endif::doctype-manpage[]
ifdef::quirks[]
include::{backend}-quirks.conf[]
endif::quirks[]

View File

@ -132,7 +132,7 @@ download_dialog_layouter(struct dialog_data *dlg_data)
&& download->progress->size >= 0); && download->progress->size >= 0);
#if CONFIG_BITTORRENT #if CONFIG_BITTORRENT
int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT
&& (show_meter || download->state == S_RESUME)); && (show_meter || is_in_state(download->state, S_RESUME)));
#endif #endif
redraw_below_window(dlg_data->win); redraw_below_window(dlg_data->win);
@ -402,7 +402,7 @@ draw_file_download(struct listbox_item *item, struct listbox_context *context,
if (!download->progress if (!download->progress
|| download->progress->size < 0 || download->progress->size < 0
|| download->state != S_TRANS || !is_in_state(download->state, S_TRANS)
|| !has_progress(download->progress)) { || !has_progress(download->progress)) {
/* TODO: Show trimmed error message. */ /* TODO: Show trimmed error message. */
return; return;

View File

@ -74,7 +74,8 @@ save_url(struct session *ses, unsigned char *url)
uri = get_translated_uri(url, ses->tab->term->cwd); uri = get_translated_uri(url, ses->tab->term->cwd);
if (!uri) { if (!uri) {
print_error_dialog(ses, S_BAD_URL, uri, PRI_CANCEL); print_error_dialog(ses, connection_state(S_BAD_URL),
uri, PRI_CANCEL);
return; return;
} }
@ -568,12 +569,14 @@ query_file(struct session *ses, struct uri *uri, void *data,
* the checking? --jonas */ * the checking? --jonas */
if (uri->protocol == PROTOCOL_UNKNOWN) { if (uri->protocol == PROTOCOL_UNKNOWN) {
print_error_dialog(ses, S_UNKNOWN_PROTOCOL, uri, PRI_CANCEL); print_error_dialog(ses, connection_state(S_UNKNOWN_PROTOCOL),
uri, PRI_CANCEL);
return; return;
} }
if (get_protocol_external_handler(ses->tab->term, uri)) { if (get_protocol_external_handler(ses->tab->term, uri)) {
print_error_dialog(ses, S_EXTERNAL_PROTOCOL, uri, PRI_CANCEL); print_error_dialog(ses, connection_state(S_EXTERNAL_PROTOCOL),
uri, PRI_CANCEL);
return; return;
} }

View File

@ -217,12 +217,12 @@ display_status_bar(struct session *ses, struct terminal *term, int tabs_count)
static int last_current_link; static int last_current_link;
int ncl = doc_view->vs->current_link; int ncl = doc_view->vs->current_link;
if (download->state == S_INTERRUPTED if (is_in_state(download->state, S_INTERRUPTED)
&& ncl != last_current_link) && ncl != last_current_link)
download->state = S_OK; download->state = connection_state(S_OK);
last_current_link = ncl; last_current_link = ncl;
if (download->state == S_OK) { if (is_in_state(download->state, S_OK)) {
if (get_current_link(doc_view)) { if (get_current_link(doc_view)) {
msg = get_current_link_info_and_title(ses, doc_view); msg = get_current_link_info_and_title(ses, doc_view);
} else if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) { } else if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
@ -344,7 +344,7 @@ display_tab_bar(struct session *ses, struct terminal *term, int tabs_count)
} else { } else {
download = get_current_download(tab_ses); download = get_current_download(tab_ses);
if (download && download->state != S_OK) { if (download && !is_in_state(download->state, S_OK)) {
color = loading_color; color = loading_color;
} else if (!tab_ses || !tab_ses->status.visited) { } else if (!tab_ses || !tab_ses->status.visited) {
color = fresh_color; color = fresh_color;

View File

@ -165,7 +165,7 @@ import_css_file(struct css_stylesheet *css, struct uri *base_uri,
add_bytes_to_string(&filename, url, urllen); add_bytes_to_string(&filename, url, urllen);
if (read_encoded_file(&filename, &string) == S_OK) { if (is_in_state(read_encoded_file(&filename, &string), S_OK)) {
unsigned char *end = string.source + string.length; unsigned char *end = string.source + string.length;
css->import_level++; css->import_level++;

View File

@ -33,8 +33,6 @@
#include "util/string.h" #include "util/string.h"
#include "viewer/text/link.h" #include "viewer/text/link.h"
static int document_init_counter = 0;
static INIT_LIST_OF(struct document, format_cache); static INIT_LIST_OF(struct document, format_cache);
struct document * struct document *
@ -56,7 +54,6 @@ init_document(struct cache_entry *cached, struct document_options *options)
#ifdef CONFIG_ECMASCRIPT #ifdef CONFIG_ECMASCRIPT
init_list(document->onload_snippets); init_list(document->onload_snippets);
document->document_id = ++document_init_counter;
#endif #endif
#ifdef CONFIG_COMBINE #ifdef CONFIG_COMBINE

View File

@ -166,7 +166,6 @@ struct document {
struct uri_list ecmascript_imports; struct uri_list ecmascript_imports;
/** used by setTimeout */ /** used by setTimeout */
timer_id_T timeout; timer_id_T timeout;
unsigned int document_id;
#endif #endif
#ifdef CONFIG_CSS #ifdef CONFIG_CSS
/** @todo FIXME: We should externally maybe using cache_entry store the /** @todo FIXME: We should externally maybe using cache_entry store the

View File

@ -390,11 +390,11 @@ render_document(struct view_state *vs, struct document_view *doc_view,
* other tab when we press ^L here? */ * other tab when we press ^L here? */
if (vs->ecmascript_fragile if (vs->ecmascript_fragile
|| (vs->ecmascript || (vs->ecmascript
&& vs->ecmascript->onload_snippets_document_id && vs->ecmascript->onload_snippets_cache_id
&& document->document_id != vs->ecmascript->onload_snippets_document_id)) && document->cache_id != vs->ecmascript->onload_snippets_cache_id))
ecmascript_reset_state(vs); ecmascript_reset_state(vs);
assert(vs->ecmascript); assert(vs->ecmascript);
vs->ecmascript->onload_snippets_document_id = document->document_id; vs->ecmascript->onload_snippets_cache_id = document->cache_id;
/* Passing of the onload_snippets pointers gives *_snippets() /* Passing of the onload_snippets pointers gives *_snippets()
* some feeling of universality, shall we ever get any other * some feeling of universality, shall we ever get any other

View File

@ -66,53 +66,6 @@ static struct option_info ecmascript_options[] = {
NULL_OPTION_INFO, NULL_OPTION_INFO,
}; };
#define NUMBER_OF_URLS_TO_REMEMBER 8
static struct {
unsigned char *url;
unsigned char *frame;
} u[NUMBER_OF_URLS_TO_REMEMBER];
static int url_index = 0;
int
ecmascript_check_url(unsigned char *url, unsigned char *frame)
{
int i;
/* Because of gradual rendering window.open is called many
* times with the same arguments.
* This workaround remembers NUMBER_OF_URLS_TO_REMEMBER last
* opened URLs and do not let open them again.
*/
for (i = 0; i < NUMBER_OF_URLS_TO_REMEMBER; i++) {
if (!u[i].url) break;
if (!strcmp(u[i].url, url) && !strcmp(u[i].frame, frame)) {
mem_free(url);
mem_free(frame);
return 0;
}
}
mem_free_if(u[url_index].url);
mem_free_if(u[url_index].frame);
u[url_index].url = url;
u[url_index].frame = frame;
url_index++;
if (url_index >= NUMBER_OF_URLS_TO_REMEMBER) url_index = 0;
return 1;
}
void
ecmascript_free_urls(struct module *module)
{
int i;
for (i = 0; i < NUMBER_OF_URLS_TO_REMEMBER; i++) {
mem_free_if(u[i].url);
mem_free_if(u[i].frame);
}
}
#undef NUMBER_OF_URLS_TO_REMEMBER
struct ecmascript_interpreter * struct ecmascript_interpreter *
ecmascript_get_interpreter(struct view_state *vs) ecmascript_get_interpreter(struct view_state *vs)
{ {
@ -401,5 +354,5 @@ struct module ecmascript_module = struct_module(
/* submodules: */ ecmascript_modules, /* submodules: */ ecmascript_modules,
/* data: */ NULL, /* data: */ NULL,
/* init: */ NULL, /* init: */ NULL,
/* done: */ ecmascript_free_urls /* done: */ NULL
); );

View File

@ -52,7 +52,7 @@ struct ecmascript_interpreter {
* ecmascript_fragile, but it can happen i.e. when the urrent document * ecmascript_fragile, but it can happen i.e. when the urrent document
* is reloaded in another tab and then you just cause the current tab * is reloaded in another tab and then you just cause the current tab
* to redraw. */ * to redraw. */
unsigned int onload_snippets_document_id; unsigned int onload_snippets_cache_id;
}; };
/* Why is the interpreter bound to {struct view_state} instead of {struct /* Why is the interpreter bound to {struct view_state} instead of {struct

View File

@ -241,14 +241,12 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
struct view_state *vs = win->vs; struct view_state *vs = win->vs;
struct document_view *doc_view = vs->doc_view; struct document_view *doc_view = vs->doc_view;
struct session *ses = doc_view->session; struct session *ses = doc_view->session;
unsigned char *frame = ""; unsigned char *frame = NULL;
unsigned char *url, *url2; unsigned char *url, *url2;
struct uri *uri; struct uri *uri;
struct SEE_value url_value; struct SEE_value url_value;
#if 0
static time_t ratelimit_start; static time_t ratelimit_start;
static int ratelimit_count; static int ratelimit_count;
#endif
/* Do not check thisobj->objectclass. ELinks sets this /* Do not check thisobj->objectclass. ELinks sets this
* function as a property of both the window object and the * function as a property of both the window object and the
@ -263,7 +261,7 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
} }
if (argc < 1) return; if (argc < 1) return;
#if 0
/* Ratelimit window opening. Recursive window.open() is very nice. /* Ratelimit window opening. Recursive window.open() is very nice.
* We permit at most 20 tabs in 2 seconds. The ratelimiter is very * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
* rough but shall suffice against the usual cases. */ * rough but shall suffice against the usual cases. */
@ -276,7 +274,7 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
if (ratelimit_count > 20) if (ratelimit_count > 20)
return; return;
} }
#endif
SEE_ToString(interp, argv[0], &url_value); SEE_ToString(interp, argv[0], &url_value);
url = see_string_to_unsigned_char(url_value.u.string); url = see_string_to_unsigned_char(url_value.u.string);
if (!url) return; if (!url) return;
@ -290,19 +288,23 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
mem_free(url); mem_free(url);
return; return;
} }
/* url and frame will be freed by ecmascript_check_url */
if (!ecmascript_check_url(url, frame)) return;
} }
/* TODO: Support for window naming and perhaps some window features? */ /* TODO: Support for window naming and perhaps some window features? */
url2 = join_urls(doc_view->document->uri, url); url2 = join_urls(doc_view->document->uri, url);
mem_free(url); mem_free(url);
if (!url2) return; if (!url2) {
mem_free_if(frame);
return;
}
uri = get_uri(url2, 0); uri = get_uri(url2, 0);
mem_free(url2); mem_free(url2);
if (!uri) return; if (!uri) {
mem_free_if(frame);
return;
}
if (*frame && strcasecmp(frame, "_blank")) { if (frame && *frame && strcasecmp(frame, "_blank")) {
struct delayed_open *deo = mem_calloc(1, sizeof(*deo)); struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
if (deo) { if (deo) {
@ -335,6 +337,7 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
end: end:
done_uri(uri); done_uri(uri);
mem_free_if(frame);
} }
static void static void

View File

@ -341,7 +341,7 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
struct view_state *vs; struct view_state *vs;
struct document_view *doc_view; struct document_view *doc_view;
struct session *ses; struct session *ses;
unsigned char *frame = ""; unsigned char *frame = NULL;
unsigned char *url, *url2; unsigned char *url, *url2;
struct uri *uri; struct uri *uri;
static time_t ratelimit_start; static time_t ratelimit_start;
@ -362,17 +362,6 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (argc < 1) return JS_TRUE; if (argc < 1) return JS_TRUE;
url = stracpy(jsval_to_string(ctx, &argv[0]));
trim_chars(url, ' ', 0);
if (argc > 1) {
frame = stracpy(jsval_to_string(ctx, &argv[1]));
if (!frame) {
mem_free(url);
return JS_TRUE;
}
if (!ecmascript_check_url(url, frame)) return JS_TRUE;
}
/* Ratelimit window opening. Recursive window.open() is very nice. /* Ratelimit window opening. Recursive window.open() is very nice.
* We permit at most 20 tabs in 2 seconds. The ratelimiter is very * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
* rough but shall suffice against the usual cases. */ * rough but shall suffice against the usual cases. */
@ -381,23 +370,36 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
ratelimit_count = 0; ratelimit_count = 0;
} else { } else {
ratelimit_count++; ratelimit_count++;
if (ratelimit_count > 20) if (ratelimit_count > 20) {
return JS_TRUE; return JS_TRUE;
}
} }
/* TODO: Support for window naming and perhaps some window features? */ url = stracpy(jsval_to_string(ctx, &argv[0]));
trim_chars(url, ' ', 0);
url2 = join_urls(doc_view->document->uri, url); url2 = join_urls(doc_view->document->uri, url);
mem_free(url); mem_free(url);
if (!url2) { if (!url2) {
return JS_TRUE; return JS_TRUE;
} }
if (argc > 1) {
frame = stracpy(jsval_to_string(ctx, &argv[1]));
if (!frame) {
mem_free(url2);
return JS_TRUE;
}
}
/* TODO: Support for window naming and perhaps some window features? */
uri = get_uri(url2, 0); uri = get_uri(url2, 0);
mem_free(url2); mem_free(url2);
if (!uri) return JS_TRUE; if (!uri) {
mem_free_if(frame);
return JS_TRUE;
}
if (frame && *frame && strcasecmp(frame, "_blank")) {
if (*frame && strcasecmp(frame, "_blank")) {
struct delayed_open *deo = mem_calloc(1, sizeof(*deo)); struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
if (deo) { if (deo) {
@ -434,6 +436,7 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
end: end:
done_uri(uri); done_uri(uri);
mem_free_if(frame);
return JS_TRUE; return JS_TRUE;
} }

View File

@ -223,10 +223,10 @@ try_encoding_extensions(struct string *filename, int *fd)
* whether the true end of the stream has been reached. * whether the true end of the stream has been reached.
* *
* @return a connection state. S_OK if all is well. */ * @return a connection state. S_OK if all is well. */
enum connection_state struct connection_state
read_file(struct stream_encoded *stream, int readsize, struct string *page) read_file(struct stream_encoded *stream, int readsize, struct string *page)
{ {
if (!init_string(page)) return S_OUT_OF_MEM; if (!init_string(page)) return connection_state(S_OUT_OF_MEM);
/* We read with granularity of stt.st_size (given as @readsize) - this /* We read with granularity of stt.st_size (given as @readsize) - this
* does best job for uncompressed files, and doesn't hurt for * does best job for uncompressed files, and doesn't hurt for
@ -248,17 +248,17 @@ read_file(struct stream_encoded *stream, int readsize, struct string *page)
* do. Since errno == 0 == S_WAIT and we cannot have * do. Since errno == 0 == S_WAIT and we cannot have
* that. */ * that. */
if (errno) if (errno)
return (enum connection_state) -errno; return connection_state_for_errno(errno);
/* FIXME: This is indeed an internal error. If readed from a /* FIXME: This is indeed an internal error. If readed from a
* corrupted encoded file nothing or only some of the * corrupted encoded file nothing or only some of the
* data will be read. */ * data will be read. */
return S_ENCODE_ERROR; return connection_state(S_ENCODE_ERROR);
} else if (readlen == 0) { } else if (readlen == 0) {
/* NUL-terminate just in case */ /* NUL-terminate just in case */
page->source[page->length] = '\0'; page->source[page->length] = '\0';
return S_OK; return connection_state(S_OK);
} }
page->length += readlen; page->length += readlen;
@ -276,7 +276,7 @@ read_file(struct stream_encoded *stream, int readsize, struct string *page)
} }
done_string(page); done_string(page);
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
} }
static inline int static inline int
@ -291,14 +291,14 @@ is_stdin_pipe(struct stat *stt, struct string *filename)
S_ISFIFO(stt->st_mode)); S_ISFIFO(stt->st_mode));
} }
enum connection_state struct connection_state
read_encoded_file(struct string *filename, struct string *page) read_encoded_file(struct string *filename, struct string *page)
{ {
struct stream_encoded *stream; struct stream_encoded *stream;
struct stat stt; struct stat stt;
enum stream_encoding encoding = ENCODING_NONE; enum stream_encoding encoding = ENCODING_NONE;
int fd = open(filename->source, O_RDONLY | O_NOCTTY); int fd = open(filename->source, O_RDONLY | O_NOCTTY);
enum connection_state state = -errno; struct connection_state state = connection_state_for_errno(errno);
if (fd == -1 && get_opt_bool("protocol.file.try_encoding_extensions", NULL)) { if (fd == -1 && get_opt_bool("protocol.file.try_encoding_extensions", NULL)) {
encoding = try_encoding_extensions(filename, &fd); encoding = try_encoding_extensions(filename, &fd);
@ -323,7 +323,7 @@ read_encoded_file(struct string *filename, struct string *page)
/* Do all the necessary checks before trying to read the file. /* Do all the necessary checks before trying to read the file.
* @state code is used to block further progress. */ * @state code is used to block further progress. */
if (fstat(fd, &stt)) { if (fstat(fd, &stt)) {
state = -errno; state = connection_state_for_errno(errno);
} else if (!S_ISREG(stt.st_mode) && encoding != ENCODING_NONE) { } else if (!S_ISREG(stt.st_mode) && encoding != ENCODING_NONE) {
/* We only want to open regular encoded files. */ /* We only want to open regular encoded files. */
@ -331,10 +331,10 @@ read_encoded_file(struct string *filename, struct string *page)
} else if (!S_ISREG(stt.st_mode) && !is_stdin_pipe(&stt, filename) } else if (!S_ISREG(stt.st_mode) && !is_stdin_pipe(&stt, filename)
&& !get_opt_bool("protocol.file.allow_special_files", NULL)) { && !get_opt_bool("protocol.file.allow_special_files", NULL)) {
state = S_FILE_TYPE; state = connection_state(S_FILE_TYPE);
} else if (!(stream = open_encoded(fd, encoding))) { } else if (!(stream = open_encoded(fd, encoding))) {
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
} else { } else {
int readsize = (int) stt.st_size; int readsize = (int) stt.st_size;
@ -343,9 +343,9 @@ read_encoded_file(struct string *filename, struct string *page)
/* FIXME: See bug 497 for info about support for big files. */ /* FIXME: See bug 497 for info about support for big files. */
if (readsize != stt.st_size || readsize < 0) { if (readsize != stt.st_size || readsize < 0) {
#ifdef EFBIG #ifdef EFBIG
state = (enum connection_state) -(EFBIG); state = connection_state_for_errno(EFBIG);
#else #else
state = S_FILE_ERROR; state = connection_state(S_FILE_ERROR);
#endif #endif
} else { } else {

View File

@ -39,10 +39,10 @@ enum stream_encoding guess_encoding(unsigned char *filename);
const unsigned char *get_encoding_name(enum stream_encoding encoding); const unsigned char *get_encoding_name(enum stream_encoding encoding);
/* Read from open @stream into the @page string */ /* Read from open @stream into the @page string */
enum connection_state struct connection_state
read_file(struct stream_encoded *stream, int readsize, struct string *page); read_file(struct stream_encoded *stream, int readsize, struct string *page);
/* Reads the file with the given @filename into the string @source. */ /* Reads the file with the given @filename into the string @source. */
enum connection_state read_encoded_file(struct string *filename, struct string *source); struct connection_state read_encoded_file(struct string *filename, struct string *source);
#endif #endif

View File

@ -228,28 +228,28 @@ check_queue_bugs(void)
#endif #endif
static void static void
set_connection_socket_state(struct socket *socket, enum connection_state state) set_connection_socket_state(struct socket *socket, struct connection_state state)
{ {
assert(socket); assert(socket);
set_connection_state(socket->conn, state); set_connection_state(socket->conn, state);
} }
static void static void
set_connection_socket_timeout(struct socket *socket, enum connection_state state) set_connection_socket_timeout(struct socket *socket, struct connection_state state)
{ {
assert(socket); assert(socket);
set_connection_timeout(socket->conn); set_connection_timeout(socket->conn);
} }
static void static void
retry_connection_socket(struct socket *socket, enum connection_state state) retry_connection_socket(struct socket *socket, struct connection_state state)
{ {
assert(socket); assert(socket);
retry_connection(socket->conn, state); retry_connection(socket->conn, state);
} }
static void static void
done_connection_socket(struct socket *socket, enum connection_state state) done_connection_socket(struct socket *socket, struct connection_state state)
{ {
assert(socket); assert(socket);
abort_connection(socket->conn, state); abort_connection(socket->conn, state);
@ -350,7 +350,7 @@ upload_stat_timer(struct connection *conn)
} }
void void
set_connection_state(struct connection *conn, enum connection_state state) set_connection_state(struct connection *conn, struct connection_state state)
{ {
struct download *download; struct download *download;
struct progress *progress = conn->progress; struct progress *progress = conn->progress;
@ -360,7 +360,7 @@ set_connection_state(struct connection *conn, enum connection_state state)
conn->prev_error = conn->state; conn->prev_error = conn->state;
conn->state = state; conn->state = state;
if (conn->state == S_TRANS) { if (is_in_state(conn->state, S_TRANS)) {
if (upload_progress && upload_progress->timer == TIMER_ID_UNDEF) { if (upload_progress && upload_progress->timer == TIMER_ID_UNDEF) {
start_update_progress(upload_progress, start_update_progress(upload_progress,
(void (*)(void *)) upload_stat_timer, conn); (void (*)(void *)) upload_stat_timer, conn);
@ -442,14 +442,14 @@ free_connection_data(struct connection *conn)
kill_timer(&conn->timer); kill_timer(&conn->timer);
if (conn->state != S_WAIT) if (!is_in_state(conn->state, S_WAIT))
done_host_connection(conn); done_host_connection(conn);
} }
static void static void
notify_connection_callbacks(struct connection *conn) notify_connection_callbacks(struct connection *conn)
{ {
enum connection_state state = conn->state; struct connection_state state = conn->state;
struct download *download, *next; struct download *download, *next;
foreachsafe (download, next, conn->downloads) { foreachsafe (download, next, conn->downloads) {
@ -469,7 +469,7 @@ done_connection(struct connection *conn)
* connection is in a result state. If it is not already it is an * connection is in a result state. If it is not already it is an
* internal bug. This should never happen but it does. ;) --jonas */ * internal bug. This should never happen but it does. ;) --jonas */
if (!is_in_result_state(conn->state)) if (!is_in_result_state(conn->state))
set_connection_state(conn, S_INTERNAL); set_connection_state(conn, connection_state(S_INTERNAL));
del_from_list(conn); del_from_list(conn);
notify_connection_callbacks(conn); notify_connection_callbacks(conn);
@ -728,7 +728,7 @@ static inline void
suspend_connection(struct connection *conn) suspend_connection(struct connection *conn)
{ {
interrupt_connection(conn); interrupt_connection(conn);
set_connection_state(conn, S_WAIT); set_connection_state(conn, connection_state(S_WAIT));
} }
static void static void
@ -742,7 +742,7 @@ run_connection(struct connection *conn)
if_assert_failed return; if_assert_failed return;
if (!add_host_connection(conn)) { if (!add_host_connection(conn)) {
set_connection_state(conn, S_OUT_OF_MEM); set_connection_state(conn, connection_state(S_OUT_OF_MEM));
done_connection(conn); done_connection(conn);
return; return;
} }
@ -755,12 +755,12 @@ run_connection(struct connection *conn)
/* Set certain state on a connection and then abort the connection. */ /* Set certain state on a connection and then abort the connection. */
void void
abort_connection(struct connection *conn, enum connection_state state) abort_connection(struct connection *conn, struct connection_state state)
{ {
assertm(is_in_result_state(state), assertm(is_in_result_state(state),
"connection didn't end in result state (%d)", state); "connection didn't end in result state (%d)", state);
if (state == S_OK && conn->cached) if (is_in_state(state, S_OK) && conn->cached)
normalize_cache_entry(conn->cached, conn->from); normalize_cache_entry(conn->cached, conn->from);
set_connection_state(conn, state); set_connection_state(conn, state);
@ -772,7 +772,7 @@ abort_connection(struct connection *conn, enum connection_state state)
/* Set certain state on a connection and then retry the connection. */ /* Set certain state on a connection and then retry the connection. */
void void
retry_connection(struct connection *conn, enum connection_state state) retry_connection(struct connection *conn, struct connection_state state)
{ {
int max_tries = get_opt_int("connection.retries", NULL); int max_tries = get_opt_int("connection.retries", NULL);
@ -799,7 +799,7 @@ try_to_suspend_connection(struct connection *conn, struct uri *uri)
foreachback (c, connection_queue) { foreachback (c, connection_queue) {
if (get_priority(c) <= priority) return -1; if (get_priority(c) <= priority) return -1;
if (c->state == S_WAIT) continue; if (is_in_state(c->state, S_WAIT)) continue;
if (c->uri->post && get_priority(c) < PRI_CANCEL) continue; if (c->uri->post && get_priority(c) < PRI_CANCEL) continue;
if (uri && !compare_uri(uri, c->uri, URI_HOST)) continue; if (uri && !compare_uri(uri, c->uri, URI_HOST)) continue;
suspend_connection(c); suspend_connection(c);
@ -844,7 +844,8 @@ again:
struct connection *cc = c; struct connection *cc = c;
c = c->next; c = c->next;
if (cc->state == S_WAIT && get_keepalive_connection(cc) if (is_in_state(cc->state, S_WAIT)
&& get_keepalive_connection(cc)
&& try_connection(cc, max_conns_to_host, max_conns)) && try_connection(cc, max_conns_to_host, max_conns))
goto again; goto again;
} }
@ -853,7 +854,7 @@ again:
struct connection *cc = c; struct connection *cc = c;
c = c->next; c = c->next;
if (cc->state == S_WAIT if (is_in_state(cc->state, S_WAIT)
&& try_connection(cc, max_conns_to_host, max_conns)) && try_connection(cc, max_conns_to_host, max_conns))
goto again; goto again;
} }
@ -863,8 +864,8 @@ again:
again2: again2:
foreachback (conn, connection_queue) { foreachback (conn, connection_queue) {
if (get_priority(conn) < PRI_CANCEL) break; if (get_priority(conn) < PRI_CANCEL) break;
if (conn->state == S_WAIT) { if (is_in_state(conn->state, S_WAIT)) {
set_connection_state(conn, S_INTERRUPTED); set_connection_state(conn, connection_state(S_INTERRUPTED));
done_connection(conn); done_connection(conn);
goto again2; goto again2;
} }
@ -886,14 +887,14 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
struct cache_entry *cached; struct cache_entry *cached;
struct connection *conn; struct connection *conn;
struct uri *proxy_uri, *proxied_uri; struct uri *proxy_uri, *proxied_uri;
enum connection_state connection_state = S_OK; struct connection_state error_state = connection_state(S_OK);
if (download) { if (download) {
download->conn = NULL; download->conn = NULL;
download->cached = NULL; download->cached = NULL;
download->pri = pri; download->pri = pri;
download->state = S_OUT_OF_MEM; download->state = connection_state(S_OUT_OF_MEM);
download->prev_error = 0; download->prev_error = connection_state(0);
} }
#ifdef CONFIG_DEBUG #ifdef CONFIG_DEBUG
@ -903,7 +904,7 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
foreach (assigned, conn->downloads) { foreach (assigned, conn->downloads) {
assertm(assigned != download, "Download assigned to '%s'", struri(conn->uri)); assertm(assigned != download, "Download assigned to '%s'", struri(conn->uri));
if_assert_failed { if_assert_failed {
download->state = S_INTERNAL; download->state = connection_state(S_INTERNAL);
if (download->callback) if (download->callback)
download->callback(download, download->data); download->callback(download, download->data);
return 0; return 0;
@ -917,7 +918,7 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
if (cached) { if (cached) {
if (download) { if (download) {
download->cached = cached; download->cached = cached;
download->state = S_OK; download->state = connection_state(S_OK);
/* XXX: /* XXX:
* This doesn't work since sometimes |download->progress| * This doesn't work since sometimes |download->progress|
* is undefined and contains random memory locations. * is undefined and contains random memory locations.
@ -934,7 +935,7 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
} }
proxied_uri = get_proxied_uri(uri); proxied_uri = get_proxied_uri(uri);
proxy_uri = get_proxy_uri(uri, &connection_state); proxy_uri = get_proxy_uri(uri, &error_state);
if (!proxy_uri if (!proxy_uri
|| !proxied_uri || !proxied_uri
@ -942,12 +943,13 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
&& !proxy_uri->hostlen)) { && !proxy_uri->hostlen)) {
if (download) { if (download) {
if (connection_state == S_OK) { if (is_in_state(error_state, S_OK)) {
connection_state = proxy_uri && proxied_uri error_state = proxy_uri && proxied_uri
? S_BAD_URL : S_OUT_OF_MEM; ? connection_state(S_BAD_URL)
: connection_state(S_OUT_OF_MEM);
} }
download->state = connection_state; download->state = error_state;
download->callback(download, download->data); download->callback(download, download->data);
} }
if (proxy_uri) done_uri(proxy_uri); if (proxy_uri) done_uri(proxy_uri);
@ -987,7 +989,7 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
conn = init_connection(proxy_uri, proxied_uri, referrer, start, cache_mode, pri); conn = init_connection(proxy_uri, proxied_uri, referrer, start, cache_mode, pri);
if (!conn) { if (!conn) {
if (download) { if (download) {
download->state = S_OUT_OF_MEM; download->state = connection_state(S_OUT_OF_MEM);
download->callback(download, download->data); download->callback(download, download->data);
} }
if (proxy_uri) done_uri(proxy_uri); if (proxy_uri) done_uri(proxy_uri);
@ -1003,12 +1005,12 @@ load_uri(struct uri *uri, struct uri *referrer, struct download *download,
download->progress = conn->progress; download->progress = conn->progress;
download->conn = conn; download->conn = conn;
download->cached = NULL; download->cached = NULL;
download->state = S_OK; download->state = connection_state(S_OK);
add_to_list(conn->downloads, download); add_to_list(conn->downloads, download);
} }
add_to_queue(conn); add_to_queue(conn);
set_connection_state(conn, S_WAIT); set_connection_state(conn, connection_state(S_WAIT));
check_queue_bugs(); check_queue_bugs();
@ -1034,7 +1036,7 @@ cancel_download(struct download *download, int interrupt)
check_queue_bugs(); check_queue_bugs();
download->state = S_INTERRUPTED; download->state = connection_state(S_INTERRUPTED);
del_from_list(download); del_from_list(download);
conn = download->conn; conn = download->conn;
@ -1048,7 +1050,7 @@ cancel_download(struct download *download, int interrupt)
conn->pri[PRI_CANCEL]++; conn->pri[PRI_CANCEL]++;
if (conn->detached || interrupt) if (conn->detached || interrupt)
abort_connection(conn, S_INTERRUPTED); abort_connection(conn, connection_state(S_INTERRUPTED));
} }
sort_queue(); sort_queue();
@ -1197,7 +1199,8 @@ void
abort_all_connections(void) abort_all_connections(void)
{ {
while (!list_empty(connection_queue)) { while (!list_empty(connection_queue)) {
abort_connection(connection_queue.next, S_INTERRUPTED); abort_connection(connection_queue.next,
connection_state(S_INTERRUPTED));
} }
abort_all_keepalive_connections(); abort_all_keepalive_connections();
@ -1210,7 +1213,7 @@ abort_background_connections(void)
foreachsafe (conn, next, connection_queue) { foreachsafe (conn, next, connection_queue) {
if (get_priority(conn) >= PRI_CANCEL) if (get_priority(conn) >= PRI_CANCEL)
abort_connection(conn, S_INTERRUPTED); abort_connection(conn, connection_state(S_INTERRUPTED));
} }
} }

View File

@ -49,8 +49,8 @@ struct connection {
unsigned int id; unsigned int id;
enum connection_state state; struct connection_state state;
enum connection_state prev_error; struct connection_state prev_error;
/* The communication socket with the other side. */ /* The communication socket with the other side. */
struct socket *socket; struct socket *socket;
@ -90,14 +90,14 @@ int get_keepalive_connections_count(void);
int get_connections_connecting_count(void); int get_connections_connecting_count(void);
int get_connections_transfering_count(void); int get_connections_transfering_count(void);
void set_connection_state(struct connection *, enum connection_state); void set_connection_state(struct connection *, struct connection_state);
int has_keepalive_connection(struct connection *); int has_keepalive_connection(struct connection *);
void add_keepalive_connection(struct connection *conn, long timeout_in_seconds, void add_keepalive_connection(struct connection *conn, long timeout_in_seconds,
void (*done)(struct connection *)); void (*done)(struct connection *));
void abort_connection(struct connection *, enum connection_state); void abort_connection(struct connection *, struct connection_state);
void retry_connection(struct connection *, enum connection_state); void retry_connection(struct connection *, struct connection_state);
void cancel_download(struct download *download, int interrupt); void cancel_download(struct download *download, int interrupt);
void move_download(struct download *old, struct download *new, void move_download(struct download *old, struct download *new,

View File

@ -49,6 +49,7 @@
#include "network/ssl/socket.h" #include "network/ssl/socket.h"
#include "osdep/osdep.h" #include "osdep/osdep.h"
#include "osdep/getifaddrs.h" #include "osdep/getifaddrs.h"
#include "protocol/http/blacklist.h"
#include "protocol/protocol.h" #include "protocol/protocol.h"
#include "protocol/uri.h" #include "protocol/uri.h"
#include "util/error.h" #include "util/error.h"
@ -65,6 +66,7 @@ struct connect_info {
void *dnsquery; /* Pointer to DNS query info. */ void *dnsquery; /* Pointer to DNS query info. */
int port; /* Which port to bind to. */ int port; /* Which port to bind to. */
int ip_family; /* If non-zero, force to IP version. */ int ip_family; /* If non-zero, force to IP version. */
struct uri *uri; /* For updating the blacklist. */
}; };
@ -104,6 +106,7 @@ init_connection_info(struct uri *uri, struct socket *socket,
connect_info->ip_family = uri->ip_family; connect_info->ip_family = uri->ip_family;
connect_info->triedno = -1; connect_info->triedno = -1;
connect_info->addr = NULL; connect_info->addr = NULL;
connect_info->uri = get_uri_reference(uri);
return connect_info; return connect_info;
} }
@ -118,6 +121,7 @@ done_connection_info(struct socket *socket)
if (connect_info->dnsquery) kill_dns_request(&connect_info->dnsquery); if (connect_info->dnsquery) kill_dns_request(&connect_info->dnsquery);
mem_free_if(connect_info->addr); mem_free_if(connect_info->addr);
done_uri(connect_info->uri);
mem_free_set(&socket->connect_info, NULL); mem_free_set(&socket->connect_info, NULL);
} }
@ -163,13 +167,13 @@ close_socket(struct socket *socket)
void void
dns_exception(struct socket *socket) dns_exception(struct socket *socket)
{ {
connect_socket(socket, S_EXCEPT); connect_socket(socket, connection_state(S_EXCEPT));
} }
static void static void
exception(struct socket *socket) exception(struct socket *socket)
{ {
socket->ops->retry(socket, S_EXCEPT); socket->ops->retry(socket, connection_state(S_EXCEPT));
} }
@ -177,23 +181,23 @@ void
timeout_socket(struct socket *socket) timeout_socket(struct socket *socket)
{ {
if (!socket->connect_info) { if (!socket->connect_info) {
socket->ops->retry(socket, S_TIMEOUT); socket->ops->retry(socket, connection_state(S_TIMEOUT));
return; return;
} }
/* Is the DNS resolving still in progress? */ /* Is the DNS resolving still in progress? */
if (socket->connect_info->dnsquery) { if (socket->connect_info->dnsquery) {
socket->ops->done(socket, S_TIMEOUT); socket->ops->done(socket, connection_state(S_TIMEOUT));
return; return;
} }
/* Try the next address, */ /* Try the next address, */
connect_socket(socket, S_TIMEOUT); connect_socket(socket, connection_state(S_TIMEOUT));
/* Reset the timeout if connect_socket() started a new attempt /* Reset the timeout if connect_socket() started a new attempt
* to connect. */ * to connect. */
if (socket->connect_info) if (socket->connect_info)
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
} }
@ -205,7 +209,7 @@ dns_found(struct socket *socket, struct sockaddr_storage *addr, int addrlen)
int size; int size;
if (!addr) { if (!addr) {
socket->ops->done(socket, S_NO_DNS); socket->ops->done(socket, connection_state(S_NO_DNS));
return; return;
} }
@ -215,7 +219,7 @@ dns_found(struct socket *socket, struct sockaddr_storage *addr, int addrlen)
connect_info->addr = mem_alloc(size); connect_info->addr = mem_alloc(size);
if (!connect_info->addr) { if (!connect_info->addr) {
socket->ops->done(socket, S_OUT_OF_MEM); socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -229,7 +233,7 @@ dns_found(struct socket *socket, struct sockaddr_storage *addr, int addrlen)
* problem if connect_socket() fails without doing any system calls * problem if connect_socket() fails without doing any system calls
* which is only the case when forcing the IP family. So it is better to * which is only the case when forcing the IP family. So it is better to
* handle it in connect_socket(). */ * handle it in connect_socket(). */
connect_socket(socket, S_CONN); connect_socket(socket, connection_state(S_CONN));
} }
void void
@ -240,17 +244,17 @@ make_connection(struct socket *socket, struct uri *uri,
struct connect_info *connect_info; struct connect_info *connect_info;
enum dns_result result; enum dns_result result;
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
if (!host) { if (!host) {
socket->ops->retry(socket, S_OUT_OF_MEM); socket->ops->retry(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
connect_info = init_connection_info(uri, socket, connect_done); connect_info = init_connection_info(uri, socket, connect_done);
if (!connect_info) { if (!connect_info) {
mem_free(host); mem_free(host);
socket->ops->retry(socket, S_OUT_OF_MEM); socket->ops->retry(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -258,6 +262,11 @@ make_connection(struct socket *socket, struct uri *uri,
/* XXX: Keep here and not in init_connection_info() to make /* XXX: Keep here and not in init_connection_info() to make
* complete_connect_socket() work from the HTTP implementation. */ * complete_connect_socket() work from the HTTP implementation. */
socket->need_ssl = get_protocol_need_ssl(uri->protocol); socket->need_ssl = get_protocol_need_ssl(uri->protocol);
if (!socket->set_no_tls) {
enum blacklist_flags flags = get_blacklist_flags(uri);
socket->no_tls = ((flags & SERVER_BLACKLIST_NO_TLS) != 0);
socket->set_no_tls = 1;
}
debug_transfer_log("\nCONNECTION: ", -1); debug_transfer_log("\nCONNECTION: ", -1);
debug_transfer_log(host, -1); debug_transfer_log(host, -1);
@ -269,7 +278,7 @@ make_connection(struct socket *socket, struct uri *uri,
mem_free(host); mem_free(host);
if (result == DNS_ASYNC) if (result == DNS_ASYNC)
socket->ops->set_state(socket, S_DNS); socket->ops->set_state(socket, connection_state(S_DNS));
} }
@ -307,7 +316,8 @@ get_pasv_socket(struct socket *ctrl_socket, struct sockaddr_storage *addr)
if (getsockname(ctrl_socket->fd, pasv_addr, &len)) { if (getsockname(ctrl_socket->fd, pasv_addr, &len)) {
sock_error: sock_error:
if (sock != -1) close(sock); if (sock != -1) close(sock);
ctrl_socket->ops->retry(ctrl_socket, -errno); ctrl_socket->ops->retry(ctrl_socket,
connection_state_for_errno(errno));
return -1; return -1;
} }
@ -425,13 +435,27 @@ complete_connect_socket(struct socket *socket, struct uri *uri,
{ {
struct connect_info *connect_info = socket->connect_info; struct connect_info *connect_info = socket->connect_info;
if (connect_info && connect_info->uri) {
/* Remember whether the server supported TLS or not.
* Then the next request can immediately use the right
* protocol. This is important for HTTP POST requests
* because it is not safe to silently retry them. The
* uri parameter is normally NULL here so don't use it. */
if (socket->no_tls)
add_blacklist_entry(connect_info->uri,
SERVER_BLACKLIST_NO_TLS);
else
del_blacklist_entry(connect_info->uri,
SERVER_BLACKLIST_NO_TLS);
}
/* This is a special case used by the HTTP implementation to acquire an /* This is a special case used by the HTTP implementation to acquire an
* SSL link for handling CONNECT requests. */ * SSL link for handling CONNECT requests. */
if (!connect_info) { if (!connect_info) {
assert(uri && socket); assert(uri && socket);
connect_info = init_connection_info(uri, socket, done); connect_info = init_connection_info(uri, socket, done);
if (!connect_info) { if (!connect_info) {
socket->ops->done(socket, S_OUT_OF_MEM); socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -459,6 +483,7 @@ static void
connected(struct socket *socket) connected(struct socket *socket)
{ {
int err = 0; int err = 0;
struct connection_state state = connection_state(0);
socklen_t len = sizeof(err); socklen_t len = sizeof(err);
assertm(socket->connect_info != NULL, "Lost connect_info!"); assertm(socket->connect_info != NULL, "Lost connect_info!");
@ -467,17 +492,21 @@ connected(struct socket *socket)
if (getsockopt(socket->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == 0) { if (getsockopt(socket->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == 0) {
/* Why does EMX return so large values? */ /* Why does EMX return so large values? */
if (err >= 10000) err -= 10000; if (err >= 10000) err -= 10000;
if (err != 0)
state = connection_state_for_errno(err);
else
state = connection_state(0);
} else { } else {
/* getsockopt() failed */ /* getsockopt() failed */
if (errno > 0) if (errno != 0)
err = errno; state = connection_state_for_errno(errno);
else else
err = -(S_STATE); state = connection_state(S_STATE);
} }
if (err > 0) { if (!is_in_state(state, 0)) {
/* There are maybe still some more candidates. */ /* There are maybe still some more candidates. */
connect_socket(socket, -err); connect_socket(socket, state);
return; return;
} }
@ -485,7 +514,7 @@ connected(struct socket *socket)
} }
void void
connect_socket(struct socket *csocket, enum connection_state state) connect_socket(struct socket *csocket, struct connection_state state)
{ {
int sock = -1; int sock = -1;
struct connect_info *connect_info = csocket->connect_info; struct connect_info *connect_info = csocket->connect_info;
@ -617,7 +646,7 @@ connect_socket(struct socket *csocket, enum connection_state state)
/* It will take some more time... */ /* It will take some more time... */
set_handlers(sock, NULL, (select_handler_T) connected, set_handlers(sock, NULL, (select_handler_T) connected,
(select_handler_T) dns_exception, csocket); (select_handler_T) dns_exception, csocket);
csocket->ops->set_state(csocket, S_CONN); csocket->ops->set_state(csocket, connection_state(S_CONN));
return; return;
} }
@ -635,7 +664,7 @@ connect_socket(struct socket *csocket, enum connection_state state)
* what matters is the last one because we do not know the * what matters is the last one because we do not know the
* previous one's errno, and the added complexity wouldn't * previous one's errno, and the added complexity wouldn't
* really be worth it. */ * really be worth it. */
csocket->ops->done(csocket, S_LOCAL_ONLY); csocket->ops->done(csocket, connection_state(S_LOCAL_ONLY));
return; return;
} }
@ -643,10 +672,10 @@ connect_socket(struct socket *csocket, enum connection_state state)
* new. Else use the S_DNS _progress_ state to make sure that no * new. Else use the S_DNS _progress_ state to make sure that no
* download callbacks will report any errors. */ * download callbacks will report any errors. */
if (trno != connect_info->triedno && !silent_fail) if (trno != connect_info->triedno && !silent_fail)
state = -errno; state = connection_state_for_errno(errno);
else if (trno == -1 && silent_fail) else if (trno == -1 && silent_fail)
/* All failed. */ /* All failed. */
state = S_NO_FORCED_DNS; state = connection_state(S_NO_FORCED_DNS);
csocket->ops->retry(csocket, state); csocket->ops->retry(csocket, state);
} }
@ -681,14 +710,14 @@ write_select(struct socket *socket)
assertm(wb != NULL, "write socket has no buffer"); assertm(wb != NULL, "write socket has no buffer");
if_assert_failed { if_assert_failed {
socket->ops->done(socket, S_INTERNAL); socket->ops->done(socket, connection_state(S_INTERNAL));
return; return;
} }
/* We are making some progress, therefore reset the timeout; ie. when /* We are making some progress, therefore reset the timeout; ie. when
* uploading large files the time needed for all the data to be sent can * uploading large files the time needed for all the data to be sent can
* easily exceed the timeout. */ * easily exceed the timeout. */
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
#if 0 #if 0
printf("ws: %d\n",wb->length-wb->pos); printf("ws: %d\n",wb->length-wb->pos);
@ -708,17 +737,17 @@ write_select(struct socket *socket)
switch (wr) { switch (wr) {
case SOCKET_CANT_WRITE: case SOCKET_CANT_WRITE:
socket->ops->retry(socket, S_CANT_WRITE); socket->ops->retry(socket, connection_state(S_CANT_WRITE));
break; break;
case SOCKET_SYSCALL_ERROR: case SOCKET_SYSCALL_ERROR:
socket->ops->retry(socket, -errno); socket->ops->retry(socket, connection_state_for_errno(errno));
break; break;
case SOCKET_INTERNAL_ERROR: case SOCKET_INTERNAL_ERROR:
/* The global errno variable is used for passing /* The global errno variable is used for passing
* internal connection_state error value. */ * internal connection_state error value. */
socket->ops->done(socket, -errno); socket->ops->done(socket, connection_state(errno));
break; break;
default: default:
@ -754,7 +783,7 @@ write_select(struct socket *socket)
void void
write_to_socket(struct socket *socket, unsigned char *data, int len, write_to_socket(struct socket *socket, unsigned char *data, int len,
enum connection_state state, socket_write_T write_done) struct connection_state state, socket_write_T write_done)
{ {
select_handler_T read_handler; select_handler_T read_handler;
struct write_buffer *wb; struct write_buffer *wb;
@ -764,11 +793,11 @@ write_to_socket(struct socket *socket, unsigned char *data, int len,
assert(len > 0); assert(len > 0);
if_assert_failed return; if_assert_failed return;
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
wb = mem_alloc(sizeof(*wb) + len); wb = mem_alloc(sizeof(*wb) + len);
if (!wb) { if (!wb) {
socket->ops->done(socket, S_OUT_OF_MEM); socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -811,14 +840,14 @@ read_select(struct socket *socket)
assertm(rb != NULL, "read socket has no buffer"); assertm(rb != NULL, "read socket has no buffer");
if_assert_failed { if_assert_failed {
socket->ops->done(socket, S_INTERNAL); socket->ops->done(socket, connection_state(S_INTERNAL));
return; return;
} }
/* We are making some progress, therefore reset the timeout; we do this /* We are making some progress, therefore reset the timeout; we do this
* for read_select() to avoid that the periodic calls to user handlers * for read_select() to avoid that the periodic calls to user handlers
* has to do it. */ * has to do it. */
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
if (!socket->duplex) if (!socket->duplex)
clear_handlers(socket->fd); clear_handlers(socket->fd);
@ -828,7 +857,7 @@ read_select(struct socket *socket)
rb = mem_realloc(rb, size); rb = mem_realloc(rb, size);
if (!rb) { if (!rb) {
socket->ops->done(socket, S_OUT_OF_MEM); socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
return; return;
} }
rb->freespace = size - sizeof(*rb) - rb->length; rb->freespace = size - sizeof(*rb) - rb->length;
@ -848,7 +877,7 @@ read_select(struct socket *socket)
switch (rd) { switch (rd) {
#ifdef CONFIG_SSL #ifdef CONFIG_SSL
case SOCKET_SSL_WANT_READ: case SOCKET_SSL_WANT_READ:
read_from_socket(socket, rb, S_TRANS, rb->done); read_from_socket(socket, rb, connection_state(S_TRANS), rb->done);
break; break;
#endif #endif
case SOCKET_CANT_READ: case SOCKET_CANT_READ:
@ -858,15 +887,17 @@ read_select(struct socket *socket)
break; break;
} }
errno = -S_CANT_READ; socket->ops->retry(socket, connection_state(S_CANT_READ));
/* Fall-through */ break;
case SOCKET_SYSCALL_ERROR: case SOCKET_SYSCALL_ERROR:
socket->ops->retry(socket, -errno); socket->ops->retry(socket, connection_state_for_errno(errno));
break; break;
case SOCKET_INTERNAL_ERROR: case SOCKET_INTERNAL_ERROR:
socket->ops->done(socket, -errno); /* The global errno variable is used for passing
* internal connection_state error value. */
socket->ops->done(socket, connection_state(errno));
break; break;
default: default:
@ -887,7 +918,7 @@ alloc_read_buffer(struct socket *socket)
rb = mem_calloc(1, RD_SIZE(rb, 0)); rb = mem_calloc(1, RD_SIZE(rb, 0));
if (!rb) { if (!rb) {
socket->ops->done(socket, S_OUT_OF_MEM); socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
return NULL; return NULL;
} }
@ -902,13 +933,13 @@ alloc_read_buffer(struct socket *socket)
void void
read_from_socket(struct socket *socket, struct read_buffer *buffer, read_from_socket(struct socket *socket, struct read_buffer *buffer,
enum connection_state state, socket_read_T done) struct connection_state state, socket_read_T done)
{ {
select_handler_T write_handler; select_handler_T write_handler;
buffer->done = done; buffer->done = done;
socket->ops->set_timeout(socket, 0); socket->ops->set_timeout(socket, connection_state(0));
socket->ops->set_state(socket, state); socket->ops->set_state(socket, state);
if (socket->read_buffer && buffer != socket->read_buffer) if (socket->read_buffer && buffer != socket->read_buffer)
@ -930,12 +961,13 @@ read_response_from_socket(struct socket *socket)
{ {
struct read_buffer *rb = alloc_read_buffer(socket); struct read_buffer *rb = alloc_read_buffer(socket);
if (rb) read_from_socket(socket, rb, S_SENT, socket->read_done); if (rb) read_from_socket(socket, rb, connection_state(S_SENT),
socket->read_done);
} }
void void
request_from_socket(struct socket *socket, unsigned char *data, int datalen, request_from_socket(struct socket *socket, unsigned char *data, int datalen,
enum connection_state state, enum socket_state sock_state, struct connection_state state, enum socket_state sock_state,
socket_read_T read_done) socket_read_T read_done)
{ {
socket->read_done = read_done; socket->read_done = read_done;

View File

@ -16,8 +16,8 @@ struct uri;
/* Use internally for error return values. */ /* Use internally for error return values. */
enum socket_error { enum socket_error {
SOCKET_SYSCALL_ERROR = -1, /* Retry with -errno state. */ SOCKET_SYSCALL_ERROR = -1, /* Retry with connection_state_for_errno(errno). */
SOCKET_INTERNAL_ERROR = -2, /* Stop with -errno state. */ SOCKET_INTERNAL_ERROR = -2, /* Stop with connection_state(errno). */
SOCKET_SSL_WANT_READ = -3, /* Try to read some more. */ SOCKET_SSL_WANT_READ = -3, /* Try to read some more. */
SOCKET_CANT_READ = -4, /* Retry with S_CANT_READ state. */ SOCKET_CANT_READ = -4, /* Retry with S_CANT_READ state. */
SOCKET_CANT_WRITE = -5, /* Retry with S_CANT_WRITE state. */ SOCKET_CANT_WRITE = -5, /* Retry with S_CANT_WRITE state. */
@ -39,7 +39,7 @@ enum socket_state {
typedef void (*socket_read_T)(struct socket *, struct read_buffer *); typedef void (*socket_read_T)(struct socket *, struct read_buffer *);
typedef void (*socket_write_T)(struct socket *); typedef void (*socket_write_T)(struct socket *);
typedef void (*socket_connect_T)(struct socket *); typedef void (*socket_connect_T)(struct socket *);
typedef void (*socket_operation_T)(struct socket *, enum connection_state state); typedef void (*socket_operation_T)(struct socket *, struct connection_state);
struct socket_operations { struct socket_operations {
/* Report change in the state of the socket. */ /* Report change in the state of the socket. */
@ -97,6 +97,7 @@ struct socket {
unsigned int protocol_family:1; /* EL_PF_INET, EL_PF_INET6 */ unsigned int protocol_family:1; /* EL_PF_INET, EL_PF_INET6 */
unsigned int need_ssl:1; /* If the socket needs SSL support */ unsigned int need_ssl:1; /* If the socket needs SSL support */
unsigned int no_tls:1; /* Internal SSL flag. */ unsigned int no_tls:1; /* Internal SSL flag. */
unsigned int set_no_tls:1; /* Was the blacklist checked yet? */
unsigned int duplex:1; /* Allow simultaneous reads & writes. */ unsigned int duplex:1; /* Allow simultaneous reads & writes. */
}; };
@ -139,7 +140,7 @@ int get_pasv_socket(struct socket *ctrl_socket, struct sockaddr_storage *addr);
/* Try to connect to the next available address or force the connection to retry /* Try to connect to the next available address or force the connection to retry
* if all has already been tried. Updates the connection state to * if all has already been tried. Updates the connection state to
* @connection_state. */ * @connection_state. */
void connect_socket(struct socket *socket, enum connection_state state); void connect_socket(struct socket *socket, struct connection_state state);
/* Used by the SSL layer when negotiating. */ /* Used by the SSL layer when negotiating. */
void dns_exception(struct socket *socket); void dns_exception(struct socket *socket);
@ -150,17 +151,17 @@ void dns_exception(struct socket *socket);
/* Reads data from @socket into @buffer. Calls @done each time new data is /* Reads data from @socket into @buffer. Calls @done each time new data is
* ready. */ * ready. */
void read_from_socket(struct socket *socket, struct read_buffer *buffer, void read_from_socket(struct socket *socket, struct read_buffer *buffer,
enum connection_state state, socket_read_T done); struct connection_state state, socket_read_T done);
/* Writes @datalen bytes from @data buffer to the passed @socket. When all data /* Writes @datalen bytes from @data buffer to the passed @socket. When all data
* is written the @done callback will be called. */ * is written the @done callback will be called. */
void write_to_socket(struct socket *socket, void write_to_socket(struct socket *socket,
unsigned char *data, int datalen, unsigned char *data, int datalen,
enum connection_state state, socket_write_T write_done); struct connection_state state, socket_write_T write_done);
/* Send request and get response. */ /* Send request and get response. */
void request_from_socket(struct socket *socket, unsigned char *data, int datalen, void request_from_socket(struct socket *socket, unsigned char *data, int datalen,
enum connection_state state, enum socket_state sock_state, struct connection_state state, enum socket_state sock_state,
socket_read_T read_done); socket_read_T read_done);
/* Initialize a read buffer. */ /* Initialize a read buffer. */

View File

@ -93,7 +93,7 @@ ssl_want_read(struct socket *socket)
#ifdef CONFIG_GNUTLS #ifdef CONFIG_GNUTLS
if (get_opt_bool("connection.ssl.cert_verify", NULL) if (get_opt_bool("connection.ssl.cert_verify", NULL)
&& gnutls_certificate_verify_peers(*((ssl_t *) socket->ssl))) { && gnutls_certificate_verify_peers(*((ssl_t *) socket->ssl))) {
socket->ops->retry(socket, S_SSL_ERROR); socket->ops->retry(socket, connection_state(S_SSL_ERROR));
return; return;
} }
#endif #endif
@ -107,8 +107,8 @@ ssl_want_read(struct socket *socket)
break; break;
default: default:
socket->no_tls = 1; socket->no_tls = !socket->no_tls;
socket->ops->retry(socket, S_SSL_ERROR); socket->ops->retry(socket, connection_state(S_SSL_ERROR));
} }
} }
@ -119,7 +119,7 @@ ssl_connect(struct socket *socket)
int ret; int ret;
if (init_ssl_connection(socket) == S_SSL_ERROR) { if (init_ssl_connection(socket) == S_SSL_ERROR) {
socket->ops->done(socket, S_SSL_ERROR); socket->ops->done(socket, connection_state(S_SSL_ERROR));
return -1; return -1;
} }
@ -173,7 +173,7 @@ ssl_connect(struct socket *socket)
switch (ret) { switch (ret) {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_READ2: case SSL_ERROR_WANT_READ2:
socket->ops->set_state(socket, S_SSL_NEG); socket->ops->set_state(socket, connection_state(S_SSL_NEG));
set_handlers(socket->fd, (select_handler_T) ssl_want_read, set_handlers(socket->fd, (select_handler_T) ssl_want_read,
NULL, (select_handler_T) dns_exception, socket); NULL, (select_handler_T) dns_exception, socket);
return -1; return -1;
@ -190,17 +190,17 @@ ssl_connect(struct socket *socket)
default: default:
if (ret != SSL_ERROR_NONE) { if (ret != SSL_ERROR_NONE) {
/* DBG("sslerr %s", gnutls_strerror(ret)); */ /* DBG("sslerr %s", gnutls_strerror(ret)); */
socket->no_tls = 1; socket->no_tls = !socket->no_tls;
} }
connect_socket(socket, S_SSL_ERROR); connect_socket(socket, connection_state(S_SSL_ERROR));
return -1; return -1;
} }
return 0; return 0;
} }
/* Return -1 on error, bytes written on success. */ /* Return enum socket_error on error, bytes written on success. */
ssize_t ssize_t
ssl_write(struct socket *socket, unsigned char *data, int len) ssl_write(struct socket *socket, unsigned char *data, int len)
{ {
@ -222,15 +222,14 @@ ssl_write(struct socket *socket, unsigned char *data, int len)
if (err == SSL_ERROR_SYSCALL) if (err == SSL_ERROR_SYSCALL)
return SOCKET_SYSCALL_ERROR; return SOCKET_SYSCALL_ERROR;
errno = -S_SSL_ERROR; errno = S_SSL_ERROR;
return SOCKET_INTERNAL_ERROR; return SOCKET_INTERNAL_ERROR;
} }
return wr; return wr;
} }
/* Return -1 on error, rd or success. */ /* Return enum socket_error on error, bytes read on success. */
ssize_t ssize_t
ssl_read(struct socket *socket, unsigned char *data, int len) ssl_read(struct socket *socket, unsigned char *data, int len)
{ {
@ -258,8 +257,7 @@ ssl_read(struct socket *socket, unsigned char *data, int len)
if (err == SSL_ERROR_SYSCALL2) if (err == SSL_ERROR_SYSCALL2)
return SOCKET_SYSCALL_ERROR; return SOCKET_SYSCALL_ERROR;
errno = -S_SSL_ERROR; errno = S_SSL_ERROR;
return SOCKET_INTERNAL_ERROR; return SOCKET_INTERNAL_ERROR;
} }

View File

@ -10,6 +10,7 @@
#elif defined(CONFIG_GNUTLS) #elif defined(CONFIG_GNUTLS)
#include <gcrypt.h> #include <gcrypt.h>
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#else #else
#error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?" #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
#endif #endif
@ -124,6 +125,8 @@ static void
init_gnutls(struct module *module) init_gnutls(struct module *module)
{ {
int ret = gnutls_global_init(); int ret = gnutls_global_init();
unsigned char *ca_file = get_opt_str("connection.ssl.trusted_ca_file",
NULL);
if (ret < 0) if (ret < 0)
INTERNAL("GNUTLS init failed: %s", gnutls_strerror(ret)); INTERNAL("GNUTLS init failed: %s", gnutls_strerror(ret));
@ -137,8 +140,16 @@ init_gnutls(struct module *module)
if (ret < 0) if (ret < 0)
INTERNAL("GNUTLS X509 credentials alloc failed: %s", INTERNAL("GNUTLS X509 credentials alloc failed: %s",
gnutls_strerror(ret)); gnutls_strerror(ret));
/* Here, we should load certificate files etc. */ /* Here, we should load certificate files etc. */
if (*ca_file) {
/* FIXME: check returned values. --witekfl */
gnutls_certificate_set_x509_trust_file(xcred, ca_file,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_verify_flags(xcred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
}
} }
static void static void
@ -152,8 +163,25 @@ done_gnutls(struct module *module)
static struct option_info gnutls_options[] = { static struct option_info gnutls_options[] = {
INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"), INIT_OPT_BOOL("connection.ssl", N_("Verify certificates"),
"cert_verify", 0, 0, "cert_verify", 0, 0,
N_("Verify the peer's SSL certificate. Note that this\n" N_("Verify the peer's SSL certificate. If you enable\n"
"probably doesn't work properly at all with GnuTLS.")), "this, set also \"Trusted CA file\".")),
/* The default value of the following option points to a file
* generated by the ca-certificates Debian package. Don't use
* CONFDIR here: if someone installs ELinks in $HOME and wants
* to have a user-specific trust list, he or she can just
* change the file name via the option manager. Distributors
* of binary packages should of course change the default to
* suit their systems.
* TODO: If the file name is relative, look in elinks_home? */
INIT_OPT_STRING("connection.ssl", N_("Trusted CA file"),
"trusted_ca_file", 0, "/etc/ssl/certs/ca-certificates.crt",
N_("The location of a file containing certificates of\n"
"trusted certification authorities in PEM format.\n"
"ELinks then trusts certificates issued by these CAs.\n"
"If you change this option or the file, you must\n"
"restart ELinks for the changes to take effect.\n"
"This option affects GnuTLS but not OpenSSL.")),
NULL_OPTION_INFO, NULL_OPTION_INFO,
}; };

View File

@ -127,6 +127,13 @@ static const struct s_msg_dsc msg_dsc[] = {
{S_BITTORRENT_BAD_URL, N_("The BitTorrent URL does not point to a valid URL")}, {S_BITTORRENT_BAD_URL, N_("The BitTorrent URL does not point to a valid URL")},
#endif #endif
/* fsp_open_session() failed but did not set errno.
* fsp_open_session() never sends anything to the FSP server,
* so this error does not mean the server itself does not work.
* More likely, there was some problem in asking a DNS server
* about the address of the FSP server. */
{S_FSP_OPEN_SESSION_UNKN, N_("FSP server not found")},
{0, NULL} {0, NULL}
}; };
@ -143,7 +150,7 @@ static INIT_LIST_OF(struct strerror_val, strerror_buf);
* It never returns NULL (if one changes that, be warn that * It never returns NULL (if one changes that, be warn that
* callers may not test for this condition) --Zas */ * callers may not test for this condition) --Zas */
unsigned char * unsigned char *
get_state_message(enum connection_state state, struct terminal *term) get_state_message(struct connection_state state, struct terminal *term)
{ {
unsigned char *e; unsigned char *e;
struct strerror_val *s; struct strerror_val *s;
@ -154,13 +161,13 @@ get_state_message(enum connection_state state, struct terminal *term)
int i; int i;
for (i = 0; msg_dsc[i].msg; i++) for (i = 0; msg_dsc[i].msg; i++)
if (msg_dsc[i].n == state) if (msg_dsc[i].n == state.basic)
return _(msg_dsc[i].msg, term); return _(msg_dsc[i].msg, term);
return unknown_error; return unknown_error;
} }
e = (unsigned char *) strerror(-state); e = (unsigned char *) strerror(state.syserr);
if (!e || !*e) return unknown_error; if (!e || !*e) return unknown_error;
len = strlen(e); len = strlen(e);

View File

@ -1,6 +1,8 @@
#ifndef EL__NETWORK_STATE_H #ifndef EL__NETWORK_STATE_H
#define EL__NETWORK_STATE_H #define EL__NETWORK_STATE_H
#include "util/error.h" /* assert() */
struct terminal; struct terminal;
enum connection_priority { enum connection_priority {
@ -15,23 +17,18 @@ enum connection_priority {
PRIORITIES, PRIORITIES,
}; };
/* Numbers < 0 and > -100000 are reserved for system errors reported via #define is_system_error(state) ((state).basic == S_ERRNO)
* errno/strerror(), see session.c and connection.c for further information. */ #define is_in_state(state,basic_) ((state).basic == (basic_))
/* WARNING: an errno value <= -100000 may cause some bad things... */ #define is_in_result_state(state) ((state).basic < 0)
/* NOTE: Winsock errors are in range 10000..11004. Hence our abs. values are #define is_in_progress_state(state) ((state).basic >= 0)
* above this. */ #define is_in_connecting_state(state) (S_WAIT < (state).basic && (state).basic < S_TRANS)
#define is_in_transfering_state(state) ((state).basic >= S_TRANS)
#define is_system_error(state) (S_OK < (state) && (state) < S_WAIT) #define is_in_queued_state(state) (is_in_connecting_state(state) || (state).basic == S_WAIT)
#define is_in_result_state(state) ((state) < 0)
#define is_in_progress_state(state) ((state) >= 0)
#define is_in_connecting_state(state) (S_WAIT < (state) && (state) < S_TRANS)
#define is_in_transfering_state(state) ((state) >= S_TRANS)
#define is_in_queued_state(state) (is_in_connecting_state(state) || (state) == S_WAIT)
/* FIXME: Namespace clash with Windows headers. */ /* FIXME: Namespace clash with Windows headers. */
#undef S_OK #undef S_OK
enum connection_state { enum connection_basic_state {
/* States >= 0 are used for connections still in progress. */ /* States >= 0 are used for connections still in progress. */
S_WAIT = 0, S_WAIT = 0,
S_DNS, S_DNS,
@ -49,6 +46,7 @@ enum connection_state {
/* State < 0 are used for the final result of a connection /* State < 0 are used for the final result of a connection
* (it's finished already and it ended up like this) */ * (it's finished already and it ended up like this) */
S_ERRNO = -1,
S_OK = -100000, S_OK = -100000,
S_INTERRUPTED = -100001, S_INTERRUPTED = -100001,
S_EXCEPT = -100002, S_EXCEPT = -100002,
@ -106,9 +104,48 @@ enum connection_state {
S_BITTORRENT_METAINFO = -100801, S_BITTORRENT_METAINFO = -100801,
S_BITTORRENT_TRACKER = -100802, S_BITTORRENT_TRACKER = -100802,
S_BITTORRENT_BAD_URL = -100803, S_BITTORRENT_BAD_URL = -100803,
S_FSP_OPEN_SESSION_UNKN = -100900,
}; };
unsigned char *get_state_message(enum connection_state state, struct terminal *term); /** Either an ELinks internal status code or an error code from the
* system. Use connection_state() or connection_state_for_errno()
* to construct objects of this type. */
struct connection_state {
/** An ELinks internal status code, or ::S_ERRNO if this
* structure holds a system error instead. */
enum connection_basic_state basic;
/** When #state is ::S_ERRNO, syserr is the saved value of
* errno. Otherwise, syserr should be 0. */
int syserr;
};
unsigned char *get_state_message(struct connection_state state, struct terminal *term);
void done_state_message(void); void done_state_message(void);
static inline struct connection_state
connection_state(enum connection_basic_state basic)
{
struct connection_state state = {0};
assert(basic != S_ERRNO);
if_assert_failed basic = S_INTERNAL;
state.basic = basic;
return state;
}
static inline struct connection_state
connection_state_for_errno(int syserr)
{
struct connection_state state = {0};
/* read_encoded_file() can pass syserr==0 here, so don't
* assert otherwise. */
state.basic = S_ERRNO;
state.syserr = syserr;
return state;
}
#endif #endif

View File

@ -119,5 +119,5 @@ about_protocol_handler(struct connection *conn)
} }
conn->cached = cached; conn->cached = cached;
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }

View File

@ -294,7 +294,7 @@ add_bittorrent_selection(struct uri *uri, int *selection, size_t size)
static INIT_LIST_OF(struct bittorrent_message, bittorrent_messages); static INIT_LIST_OF(struct bittorrent_message, bittorrent_messages);
void void
add_bittorrent_message(struct uri *uri, enum connection_state state, add_bittorrent_message(struct uri *uri, struct connection_state state,
struct bittorrent_const_string *string) struct bittorrent_const_string *string)
{ {
struct bittorrent_message *message; struct bittorrent_message *message;

View File

@ -16,7 +16,7 @@ uint32_t get_bittorrent_peerwire_max_request_length(void);
int *get_bittorrent_selection(struct uri *uri, size_t size); int *get_bittorrent_selection(struct uri *uri, size_t size);
void add_bittorrent_selection(struct uri *uri, int *selection, size_t size); void add_bittorrent_selection(struct uri *uri, int *selection, size_t size);
void add_bittorrent_message(struct uri *uri, enum connection_state state, void add_bittorrent_message(struct uri *uri, struct connection_state state,
struct bittorrent_const_string *); struct bittorrent_const_string *);
#endif #endif

View File

@ -333,13 +333,14 @@ bittorrent_fetch_callback(struct download *download, void *data)
struct cache_entry *cached = download->cached; struct cache_entry *cached = download->cached;
/* If the callback was removed we should shutdown ASAP. */ /* If the callback was removed we should shutdown ASAP. */
if (!fetcher->callback || download->state == S_INTERRUPTED) { if (!fetcher->callback || is_in_state(download->state, S_INTERRUPTED)) {
if (download->state == S_INTERRUPTED) if (is_in_state(download->state, S_INTERRUPTED))
mem_free(fetcher); mem_free(fetcher);
return; return;
} }
if (is_in_result_state(download->state) && download->state != S_OK) { if (is_in_result_state(download->state)
&& !is_in_state(download->state, S_OK)) {
fetcher->callback(fetcher->data, download->state, NULL); fetcher->callback(fetcher->data, download->state, NULL);
if (fetcher->ref) if (fetcher->ref)
*fetcher->ref = NULL; *fetcher->ref = NULL;
@ -353,7 +354,7 @@ bittorrent_fetch_callback(struct download *download, void *data)
if (cached->redirect && fetcher->redirects++ < MAX_REDIRECTS) { if (cached->redirect && fetcher->redirects++ < MAX_REDIRECTS) {
cancel_download(download, 0); cancel_download(download, 0);
download->state = S_WAIT_REDIR; download->state = connection_state(S_WAIT_REDIR);
load_uri(cached->redirect, cached->uri, download, load_uri(cached->redirect, cached->uri, download,
PRI_DOWNLOAD, CACHE_MODE_NORMAL, PRI_DOWNLOAD, CACHE_MODE_NORMAL,
@ -365,13 +366,13 @@ bittorrent_fetch_callback(struct download *download, void *data)
if (is_in_progress_state(download->state)) if (is_in_progress_state(download->state))
return; return;
assert(download->state == S_OK); assert(is_in_state(download->state, S_OK));
/* If the entry is chunked defragment it and grab the single, remaining /* If the entry is chunked defragment it and grab the single, remaining
* fragment. */ * fragment. */
fragment = get_cache_fragment(cached); fragment = get_cache_fragment(cached);
if (!fragment) { if (!fragment) {
fetcher->callback(fetcher->data, S_OUT_OF_MEM, NULL); fetcher->callback(fetcher->data, connection_state(S_OUT_OF_MEM), NULL);
if (fetcher->ref) if (fetcher->ref)
*fetcher->ref = NULL; *fetcher->ref = NULL;
mem_free(fetcher); mem_free(fetcher);
@ -381,7 +382,7 @@ bittorrent_fetch_callback(struct download *download, void *data)
response.source = fragment->data; response.source = fragment->data;
response.length = fragment->length; response.length = fragment->length;
fetcher->callback(fetcher->data, S_OK, &response); fetcher->callback(fetcher->data, connection_state(S_OK), &response);
if (fetcher->delete) if (fetcher->delete)
delete_cache_entry(cached); delete_cache_entry(cached);
@ -399,7 +400,7 @@ init_bittorrent_fetch(struct bittorrent_fetcher **fetcher_ref,
fetcher = mem_calloc(1, sizeof(*fetcher)); fetcher = mem_calloc(1, sizeof(*fetcher));
if (!fetcher) { if (!fetcher) {
callback(data, S_OUT_OF_MEM, NULL); callback(data, connection_state(S_OUT_OF_MEM), NULL);
return NULL; return NULL;
} }

View File

@ -68,7 +68,7 @@ struct bittorrent_message {
LIST_HEAD(struct bittorrent_message); LIST_HEAD(struct bittorrent_message);
struct uri *uri; struct uri *uri;
enum connection_state state; struct connection_state state;
unsigned char string[1]; unsigned char string[1];
}; };
@ -411,7 +411,7 @@ del_bittorrent_peer_request(struct bittorrent_peer_status *status,
/* URI fetching: */ /* URI fetching: */
/* ************************************************************************** */ /* ************************************************************************** */
typedef void (*bittorrent_fetch_callback_T)(void *, enum connection_state, typedef void (*bittorrent_fetch_callback_T)(void *, struct connection_state,
struct bittorrent_const_string *); struct bittorrent_const_string *);
struct bittorrent_fetcher * struct bittorrent_fetcher *

View File

@ -305,22 +305,22 @@ init_bittorrent_connection(struct connection *conn)
void void
bittorrent_resume_callback(struct bittorrent_connection *bittorrent) bittorrent_resume_callback(struct bittorrent_connection *bittorrent)
{ {
enum connection_state state; struct connection_state state;
/* Failing to create the listening socket is fatal. */ /* Failing to create the listening socket is fatal. */
state = init_bittorrent_listening_socket(bittorrent->conn); state = init_bittorrent_listening_socket(bittorrent->conn);
if (state != S_OK) { if (!is_in_state(state, S_OK)) {
retry_connection(bittorrent->conn, state); retry_connection(bittorrent->conn, state);
return; return;
} }
set_connection_state(bittorrent->conn, S_CONN_TRACKER); set_connection_state(bittorrent->conn, connection_state(S_CONN_TRACKER));
send_bittorrent_tracker_request(bittorrent->conn); send_bittorrent_tracker_request(bittorrent->conn);
} }
/* Metainfo file download callback */ /* Metainfo file download callback */
static void static void
bittorrent_metainfo_callback(void *data, enum connection_state state, bittorrent_metainfo_callback(void *data, struct connection_state state,
struct bittorrent_const_string *response) struct bittorrent_const_string *response)
{ {
struct connection *conn = data; struct connection *conn = data;
@ -328,7 +328,7 @@ bittorrent_metainfo_callback(void *data, enum connection_state state,
bittorrent->fetch = NULL; bittorrent->fetch = NULL;
if (state != S_OK) { if (!is_in_state(state, S_OK)) {
abort_connection(conn, state); abort_connection(conn, state);
return; return;
} }
@ -358,21 +358,22 @@ bittorrent_metainfo_callback(void *data, enum connection_state state,
return; return;
case BITTORRENT_STATE_CACHE_RESUME: case BITTORRENT_STATE_CACHE_RESUME:
set_connection_state(bittorrent->conn, S_RESUME); set_connection_state(bittorrent->conn,
connection_state(S_RESUME));
return; return;
case BITTORRENT_STATE_OUT_OF_MEM: case BITTORRENT_STATE_OUT_OF_MEM:
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
break; break;
default: default:
state = S_BITTORRENT_ERROR; state = connection_state(S_BITTORRENT_ERROR);
} }
break; break;
} }
case BITTORRENT_STATE_OUT_OF_MEM: case BITTORRENT_STATE_OUT_OF_MEM:
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
break; break;
case BITTORRENT_STATE_ERROR: case BITTORRENT_STATE_ERROR:
@ -382,7 +383,7 @@ bittorrent_metainfo_callback(void *data, enum connection_state state,
* looking at the protocol header, however, direct usage of the * looking at the protocol header, however, direct usage of the
* internal bittorrent: is at your own risk ... at least for * internal bittorrent: is at your own risk ... at least for
* now. --jonas */ * now. --jonas */
state = S_BITTORRENT_METAINFO; state = connection_state(S_BITTORRENT_METAINFO);
} }
abort_connection(conn, state); abort_connection(conn, state);
@ -397,7 +398,7 @@ bittorrent_protocol_handler(struct connection *conn)
bittorrent = init_bittorrent_connection(conn); bittorrent = init_bittorrent_connection(conn);
if (!bittorrent) { if (!bittorrent) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -405,11 +406,11 @@ bittorrent_protocol_handler(struct connection *conn)
uri = get_uri(conn->uri->data, 0); uri = get_uri(conn->uri->data, 0);
if (!uri) { if (!uri) {
abort_connection(conn, S_BITTORRENT_BAD_URL); abort_connection(conn, connection_state(S_BITTORRENT_BAD_URL));
return; return;
} }
set_connection_state(conn, S_CONN); set_connection_state(conn, connection_state(S_CONN));
set_connection_timeout(conn); set_connection_timeout(conn);
conn->from = 0; conn->from = 0;

View File

@ -531,7 +531,7 @@ draw_bittorrent_piece_progress(struct download *download, struct terminal *term,
} }
} }
if (download->state == S_RESUME) { if (is_in_state(download->state, S_RESUME)) {
static unsigned char s[] = "????"; /* Reduce or enlarge at will. */ static unsigned char s[] = "????"; /* Reduce or enlarge at will. */
unsigned int slen = 0; unsigned int slen = 0;
int max = int_min(sizeof(s), width) - 1; int max = int_min(sizeof(s), width) - 1;
@ -587,9 +587,10 @@ bittorrent_message_dialog(struct session *ses, void *data)
add_to_string(&string, ":\n\n"); add_to_string(&string, ":\n\n");
} }
if (message->state != S_OK) { if (!is_in_state(message->state, S_OK)) {
add_format_to_string(&string, "%s: %s", add_format_to_string(&string, "%s: %s",
get_state_message(S_BITTORRENT_TRACKER, ses->tab->term), get_state_message(connection_state(S_BITTORRENT_TRACKER),
ses->tab->term),
get_state_message(message->state, ses->tab->term)); get_state_message(message->state, ses->tab->term));
} else { } else {
add_to_string(&string, message->string); add_to_string(&string, message->string);
@ -686,7 +687,7 @@ tp_show_header(struct dialog_data *dlg_data, struct widget_data *widget_data)
/* Build a dialog querying the user on how to handle a .torrent file. */ /* Build a dialog querying the user on how to handle a .torrent file. */
static void static void
bittorrent_query_callback(void *data, enum connection_state state, bittorrent_query_callback(void *data, struct connection_state state,
struct bittorrent_const_string *response) struct bittorrent_const_string *response)
{ {
/* [gettext_accelerator_context(.bittorrent_query_callback)] */ /* [gettext_accelerator_context(.bittorrent_query_callback)] */
@ -705,7 +706,7 @@ bittorrent_query_callback(void *data, enum connection_state state,
struct string msg; struct string msg;
int files; int files;
if (state != S_OK) if (!is_in_state(state, S_OK))
return; return;
/* This should never happen, since setup_download_handler() should make /* This should never happen, since setup_download_handler() should make
@ -739,7 +740,8 @@ bittorrent_query_callback(void *data, enum connection_state state,
done_string(&filename); done_string(&filename);
if (parse_bittorrent_metafile(&meta, response) != BITTORRENT_STATE_OK) { if (parse_bittorrent_metafile(&meta, response) != BITTORRENT_STATE_OK) {
print_error_dialog(type_query->ses, S_BITTORRENT_METAINFO, print_error_dialog(type_query->ses,
connection_state(S_BITTORRENT_METAINFO),
type_query->uri, PRI_CANCEL); type_query->uri, PRI_CANCEL);
tp_cancel(type_query); tp_cancel(type_query);
done_string(&msg); done_string(&msg);

View File

@ -67,7 +67,7 @@ find_bittorrent_connection(bittorrent_id_T info_hash)
static void static void
check_bittorrent_peer_blacklisting(struct bittorrent_peer_connection *peer, check_bittorrent_peer_blacklisting(struct bittorrent_peer_connection *peer,
enum connection_state state) struct connection_state state)
{ {
enum bittorrent_blacklist_flags flags = BITTORRENT_BLACKLIST_NONE; enum bittorrent_blacklist_flags flags = BITTORRENT_BLACKLIST_NONE;
@ -75,21 +75,25 @@ check_bittorrent_peer_blacklisting(struct bittorrent_peer_connection *peer,
|| !get_opt_bool("protocol.http.bugs.allow_blacklist", NULL)) || !get_opt_bool("protocol.http.bugs.allow_blacklist", NULL))
return; return;
switch (state) { if (is_system_error(state)) {
case -ECONNREFUSED: switch (state.syserr) {
case -ENETUNREACH: case ECONNREFUSED:
flags |= BITTORRENT_BLACKLIST_PEER_POOL; case ENETUNREACH:
break;
case S_CANT_WRITE:
case S_CANT_READ:
if (!peer->local.handshake
|| !peer->remote.handshake)
flags |= BITTORRENT_BLACKLIST_PEER_POOL; flags |= BITTORRENT_BLACKLIST_PEER_POOL;
break; break;
}
} else {
switch (state.basic) {
case S_CANT_WRITE:
case S_CANT_READ:
if (!peer->local.handshake
|| !peer->remote.handshake)
flags |= BITTORRENT_BLACKLIST_PEER_POOL;
break;
default: default:
break; break;
}
} }
if (flags != BITTORRENT_BLACKLIST_NONE) { if (flags != BITTORRENT_BLACKLIST_NONE) {
@ -137,30 +141,30 @@ set_bittorrent_peer_connection_timeout(struct bittorrent_peer_connection *peer)
* S_DMS (while looking up the host) then moves to S_CONN (while connecting), * S_DMS (while looking up the host) then moves to S_CONN (while connecting),
* and should hopefully become S_TRANS (while transfering). Note, state can hold * and should hopefully become S_TRANS (while transfering). Note, state can hold
* both internally defined connection states as described above and errno * both internally defined connection states as described above and errno
* values, such as ECONNREFUSED. The errno values are passed negative so in the * values, such as ECONNREFUSED. */
* previous example the errno would be passed as -ECONNREFUSED. */
static void static void
set_bittorrent_socket_state(struct socket *socket, enum connection_state state) set_bittorrent_socket_state(struct socket *socket, struct connection_state state)
{ {
struct bittorrent_peer_connection *peer = socket->conn; struct bittorrent_peer_connection *peer = socket->conn;
if (state == S_TRANS && peer->bittorrent) if (is_in_state(state, S_TRANS) && peer->bittorrent)
set_connection_state(peer->bittorrent->conn, S_TRANS); set_connection_state(peer->bittorrent->conn,
connection_state(S_TRANS));
} }
/* Called when progress is made such as when the select() loop detects and /* Called when progress is made such as when the select() loop detects and
* schedules reads and writes. The state variable must be ignored. */ * schedules reads and writes. The state variable must be ignored. */
static void static void
set_bittorrent_socket_timeout(struct socket *socket, enum connection_state state) set_bittorrent_socket_timeout(struct socket *socket, struct connection_state state)
{ {
assert(state == 0); assert(is_in_state(state, 0));
set_bittorrent_peer_connection_timeout(socket->conn); set_bittorrent_peer_connection_timeout(socket->conn);
} }
/* Called when a non-fatal error condition has appeared, i.e. the condition is /* Called when a non-fatal error condition has appeared, i.e. the condition is
* caused by some internal or local system error or simply a timeout. */ * caused by some internal or local system error or simply a timeout. */
static void static void
retry_bittorrent_socket(struct socket *socket, enum connection_state state) retry_bittorrent_socket(struct socket *socket, struct connection_state state)
{ {
struct bittorrent_peer_connection *peer = socket->conn; struct bittorrent_peer_connection *peer = socket->conn;
@ -177,7 +181,7 @@ retry_bittorrent_socket(struct socket *socket, enum connection_state state)
/* Called when a fatal and unrecoverable error condition has appeared, such as a /* Called when a fatal and unrecoverable error condition has appeared, such as a
* DNS query failed. */ * DNS query failed. */
static void static void
done_bittorrent_socket(struct socket *socket, enum connection_state state) done_bittorrent_socket(struct socket *socket, struct connection_state state)
{ {
struct bittorrent_peer_connection *peer = socket->conn; struct bittorrent_peer_connection *peer = socket->conn;
@ -344,7 +348,7 @@ accept_bittorrent_peer_connection(void *____)
buffer = alloc_read_buffer(peer->socket); buffer = alloc_read_buffer(peer->socket);
if (!buffer) return; if (!buffer) return;
read_from_socket(peer->socket, buffer, S_TRANS, read_from_socket(peer->socket, buffer, connection_state(S_TRANS),
read_bittorrent_peer_handshake); read_bittorrent_peer_handshake);
add_to_list(bittorrent_peer_connections, peer); add_to_list(bittorrent_peer_connections, peer);
@ -352,7 +356,7 @@ accept_bittorrent_peer_connection(void *____)
/* Based on network/socket.c:get_pasv_socket() but modified to try and bind to a /* Based on network/socket.c:get_pasv_socket() but modified to try and bind to a
* port range instead of any port. */ * port range instead of any port. */
enum connection_state struct connection_state
init_bittorrent_listening_socket(struct connection *conn) init_bittorrent_listening_socket(struct connection *conn)
{ {
struct bittorrent_connection *bittorrent = conn->info; struct bittorrent_connection *bittorrent = conn->info;
@ -366,7 +370,7 @@ init_bittorrent_listening_socket(struct connection *conn)
/* Has the socket already been initialized? */ /* Has the socket already been initialized? */
if (!list_is_singleton(bittorrent_connections)) if (!list_is_singleton(bittorrent_connections))
return S_OK; return connection_state(S_OK);
/* We could have bailed out from an earlier attempt. */ /* We could have bailed out from an earlier attempt. */
if (bittorrent_socket != -1) if (bittorrent_socket != -1)
@ -374,12 +378,12 @@ init_bittorrent_listening_socket(struct connection *conn)
bittorrent_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); bittorrent_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (bittorrent_socket < 0) if (bittorrent_socket < 0)
return -errno; return connection_state_for_errno(errno);
/* Set it non-blocking */ /* Set it non-blocking */
if (set_nonblocking_fd(bittorrent_socket) < 0) if (set_nonblocking_fd(bittorrent_socket) < 0)
return -errno; return connection_state_for_errno(errno);
/* Bind it to some port */ /* Bind it to some port */
@ -392,11 +396,11 @@ init_bittorrent_listening_socket(struct connection *conn)
/* Repeatedly try the configured port range. */ /* Repeatedly try the configured port range. */
while (bind(bittorrent_socket, (struct sockaddr *) &addr, sizeof(addr))) { while (bind(bittorrent_socket, (struct sockaddr *) &addr, sizeof(addr))) {
if (errno != EADDRINUSE) if (errno != EADDRINUSE)
return -errno; return connection_state_for_errno(errno);
/* If all ports was in use fail with EADDRINUSE. */ /* If all ports was in use fail with EADDRINUSE. */
if (++port > max_port) if (++port > max_port)
return -errno; return connection_state_for_errno(errno);
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(port); addr.sin_port = htons(port);
@ -407,20 +411,20 @@ init_bittorrent_listening_socket(struct connection *conn)
memset(&addr2, 0, sizeof(addr2)); memset(&addr2, 0, sizeof(addr2));
len = sizeof(addr2); len = sizeof(addr2);
if (getsockname(bittorrent_socket, (struct sockaddr *) &addr2, &len)) if (getsockname(bittorrent_socket, (struct sockaddr *) &addr2, &len))
return -errno; return connection_state_for_errno(errno);
bittorrent->port = ntohs(addr2.sin_port); bittorrent->port = ntohs(addr2.sin_port);
/* Go listen */ /* Go listen */
if (listen(bittorrent_socket, LISTEN_BACKLOG)) if (listen(bittorrent_socket, LISTEN_BACKLOG))
return -errno; return connection_state_for_errno(errno);
set_ip_tos_throughput(bittorrent_socket); set_ip_tos_throughput(bittorrent_socket);
set_handlers(bittorrent_socket, accept_bittorrent_peer_connection, set_handlers(bittorrent_socket, accept_bittorrent_peer_connection,
NULL, NULL, NULL); NULL, NULL, NULL);
return S_OK; return connection_state(S_OK);
} }
void void

View File

@ -8,7 +8,7 @@
struct connection; struct connection;
/* Sets up and tears down the peer listening socket. */ /* Sets up and tears down the peer listening socket. */
enum connection_state init_bittorrent_listening_socket(struct connection *conn); struct connection_state init_bittorrent_listening_socket(struct connection *conn);
void done_bittorrent_listening_socket(struct connection *conn); void done_bittorrent_listening_socket(struct connection *conn);
void done_bittorrent_peer_connection(struct bittorrent_peer_connection *peer); void done_bittorrent_peer_connection(struct bittorrent_peer_connection *peer);

View File

@ -349,7 +349,8 @@ do_send_bittorrent_peer_message(struct bittorrent_peer_connection *peer,
} }
write_to_socket(peer->socket, string.source, string.length, write_to_socket(peer->socket, string.source, string.length,
S_TRANS, sent_bittorrent_peer_message); connection_state(S_TRANS),
sent_bittorrent_peer_message);
done_string(&string); done_string(&string);
@ -658,7 +659,8 @@ read_bittorrent_peer_data(struct socket *socket, struct read_buffer *buffer)
break; break;
case BITTORRENT_STATE_OUT_OF_MEM: case BITTORRENT_STATE_OUT_OF_MEM:
abort_connection(peer->bittorrent->conn, S_OUT_OF_MEM); abort_connection(peer->bittorrent->conn,
connection_state(S_OUT_OF_MEM));
return; return;
case BITTORRENT_STATE_ERROR: case BITTORRENT_STATE_ERROR:
@ -669,7 +671,8 @@ read_bittorrent_peer_data(struct socket *socket, struct read_buffer *buffer)
} }
/* Shutdown on fatal errors! */ /* Shutdown on fatal errors! */
abort_connection(peer->bittorrent->conn, -write_errno); abort_connection(peer->bittorrent->conn,
connection_state_for_errno(write_errno));
return; return;
} }
@ -710,7 +713,7 @@ sent_bittorrent_peer_handshake(struct socket *socket)
send_bittorrent_peer_message(peer, BITTORRENT_MESSAGE_BITFIELD); send_bittorrent_peer_message(peer, BITTORRENT_MESSAGE_BITFIELD);
} }
read_from_socket(peer->socket, buffer, S_TRANS, read_from_socket(peer->socket, buffer, connection_state(S_TRANS),
read_bittorrent_peer_data); read_bittorrent_peer_data);
} }
@ -763,7 +766,8 @@ send_bittorrent_peer_handshake(struct socket *socket)
* and we might want to hold on to the old buffer if the peer ID of the * and we might want to hold on to the old buffer if the peer ID of the
* handshake was not read. */ * handshake was not read. */
write_to_socket(peer->socket, handshake, sizeof(handshake), write_to_socket(peer->socket, handshake, sizeof(handshake),
S_TRANS, sent_bittorrent_peer_handshake); connection_state(S_TRANS),
sent_bittorrent_peer_handshake);
} }
#if 0 #if 0
@ -935,7 +939,8 @@ do_read_bittorrent_peer_handshake(struct socket *socket, struct read_buffer *buf
break; break;
} }
read_from_socket(peer->socket, buffer, S_TRANS, read_from_socket(peer->socket, buffer,
connection_state(S_TRANS),
read_bittorrent_peer_handshake); read_bittorrent_peer_handshake);
break; break;
@ -945,7 +950,8 @@ do_read_bittorrent_peer_handshake(struct socket *socket, struct read_buffer *buf
case BITTORRENT_PEER_HANDSHAKE_INCOMPLETE: case BITTORRENT_PEER_HANDSHAKE_INCOMPLETE:
/* The whole handshake was not read so wait for more. */ /* The whole handshake was not read so wait for more. */
read_from_socket(peer->socket, buffer, S_TRANS, read_from_socket(peer->socket, buffer,
connection_state(S_TRANS),
read_bittorrent_peer_handshake); read_bittorrent_peer_handshake);
break; break;
} }

View File

@ -61,7 +61,7 @@ set_bittorrent_tracker_interval(struct connection *conn)
/* XXX: The data pointer may be NULL when doing event=stopped because no /* XXX: The data pointer may be NULL when doing event=stopped because no
* connection is attached anymore. */ * connection is attached anymore. */
static void static void
bittorrent_tracker_callback(void *data, enum connection_state state, bittorrent_tracker_callback(void *data, struct connection_state state,
struct bittorrent_const_string *response) struct bittorrent_const_string *response)
{ {
struct connection *conn = data; struct connection *conn = data;
@ -76,12 +76,12 @@ bittorrent_tracker_callback(void *data, enum connection_state state,
/* FIXME: We treat any error as fatal here, however, it might be better /* FIXME: We treat any error as fatal here, however, it might be better
* to relax that and allow a few errors before ending the connection. */ * to relax that and allow a few errors before ending the connection. */
if (state != S_OK) { if (!is_in_state(state, S_OK)) {
if (state == S_INTERRUPTED) if (is_in_state(state, S_INTERRUPTED))
return; return;
bittorrent->tracker.failed = 1; bittorrent->tracker.failed = 1;
add_bittorrent_message(conn->uri, state, NULL); add_bittorrent_message(conn->uri, state, NULL);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
@ -103,16 +103,17 @@ bittorrent_tracker_callback(void *data, enum connection_state state,
return; return;
case BITTORRENT_STATE_OUT_OF_MEM: case BITTORRENT_STATE_OUT_OF_MEM:
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
break; break;
case BITTORRENT_STATE_REQUEST_FAILURE: case BITTORRENT_STATE_REQUEST_FAILURE:
add_bittorrent_message(conn->uri, S_OK, response); add_bittorrent_message(conn->uri, connection_state(S_OK),
state = S_OK; response);
state = connection_state(S_OK);
break; break;
default: default:
state = S_BITTORRENT_TRACKER; state = connection_state(S_BITTORRENT_TRACKER);
} }
abort_connection(conn, state); abort_connection(conn, state);
@ -159,7 +160,8 @@ do_send_bittorrent_tracker_request(struct connection *conn)
if (!init_string(&request)) { if (!init_string(&request)) {
done_string(&request); done_string(&request);
if (!stopped) if (!stopped)
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn,
connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -170,7 +172,8 @@ do_send_bittorrent_tracker_request(struct connection *conn)
if (!uri) { if (!uri) {
done_string(&request); done_string(&request);
if (!stopped) if (!stopped)
abort_connection(conn, S_BITTORRENT_ERROR); abort_connection(conn,
connection_state(S_BITTORRENT_ERROR));
return; return;
} }
@ -249,7 +252,8 @@ do_send_bittorrent_tracker_request(struct connection *conn)
done_string(&request); done_string(&request);
if (!uri) { if (!uri) {
if (!stopped) if (!stopped)
abort_connection(conn, S_BITTORRENT_ERROR); abort_connection(conn,
connection_state(S_BITTORRENT_ERROR));
return; return;
} }

View File

@ -43,7 +43,7 @@ close_all_non_term_fd(void)
close(n); close(n);
} }
enum connection_state struct connection_state
init_directory_listing(struct string *page, struct uri *uri) init_directory_listing(struct string *page, struct uri *uri)
{ {
struct string dirpath = NULL_STRING; struct string dirpath = NULL_STRING;
@ -151,5 +151,7 @@ out_of_memory:
done_string(&decoded); done_string(&decoded);
done_string(&location); done_string(&location);
return page->length > 0 ? S_OK : S_OUT_OF_MEM; return page->length > 0
? connection_state(S_OK)
: connection_state(S_OUT_OF_MEM);
} }

View File

@ -9,7 +9,7 @@ struct uri;
/* Close all non-terminal file descriptors. */ /* Close all non-terminal file descriptors. */
void close_all_non_term_fd(void); void close_all_non_term_fd(void);
enum connection_state struct connection_state
init_directory_listing(struct string *page, struct uri *uri); init_directory_listing(struct string *page, struct uri *uri);
#endif #endif

View File

@ -120,7 +120,7 @@ data_protocol_handler(struct connection *conn)
int base64 = 0; int base64 = 0;
if (!cached) { if (!cached) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -128,7 +128,7 @@ data_protocol_handler(struct connection *conn)
data_start = parse_data_protocol_header(conn, &base64); data_start = parse_data_protocol_header(conn, &base64);
if (!data_start) { if (!data_start) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -136,7 +136,7 @@ data_protocol_handler(struct connection *conn)
* it. */ * it. */
data = memacpy(data_start, uri->datalen - (data_start - uri->data)); data = memacpy(data_start, uri->datalen - (data_start - uri->data));
if (!data) { if (!data) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -144,7 +144,7 @@ data_protocol_handler(struct connection *conn)
unsigned char *decoded = base64_encode(data); unsigned char *decoded = base64_encode(data);
if (!decoded) { if (!decoded) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -163,5 +163,5 @@ data_protocol_handler(struct connection *conn)
mem_free(data); mem_free(data);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }

View File

@ -78,7 +78,8 @@ close_pipe_and_read(struct socket *data_socket)
data_socket->fd = -1; data_socket->fd = -1;
conn->socket->state = SOCKET_END_ONCLOSE; conn->socket->state = SOCKET_END_ONCLOSE;
read_from_socket(conn->socket, rb, S_SENT, http_got_header); read_from_socket(conn->socket, rb, connection_state(S_SENT),
http_got_header);
} }
@ -91,13 +92,13 @@ send_more_post_data(struct socket *socket)
struct http_connection_info *http = conn->info; struct http_connection_info *http = conn->info;
unsigned char buffer[POST_BUFFER_SIZE]; unsigned char buffer[POST_BUFFER_SIZE];
int got; int got;
enum connection_state error; struct connection_state error;
got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error); got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error);
if (got < 0) { if (got < 0) {
abort_connection(conn, error); abort_connection(conn, error);
} else if (got > 0) { } else if (got > 0) {
write_to_socket(socket, buffer, got, S_TRANS, write_to_socket(socket, buffer, got, connection_state(S_TRANS),
send_more_post_data); send_more_post_data);
} else { /* got == 0, meaning end of data */ } else { /* got == 0, meaning end of data */
close_pipe_and_read(socket); close_pipe_and_read(socket);
@ -112,7 +113,7 @@ send_post_data(struct connection *conn)
struct http_connection_info *http = conn->info; struct http_connection_info *http = conn->info;
unsigned char *post = conn->uri->post; unsigned char *post = conn->uri->post;
unsigned char *postend; unsigned char *postend;
enum connection_state error; struct connection_state error;
postend = strchr(post, '\n'); postend = strchr(post, '\n');
if (postend) post = postend + 1; if (postend) post = postend + 1;
@ -301,7 +302,7 @@ execute_cgi(struct connection *conn)
unsigned char *script; unsigned char *script;
struct stat buf; struct stat buf;
pid_t pid; pid_t pid;
enum connection_state state = S_OK; struct connection_state state = connection_state(S_OK);
int pipe_read[2], pipe_write[2]; int pipe_read[2], pipe_write[2];
if (!get_opt_bool("protocol.file.cgi.policy", NULL)) return 1; if (!get_opt_bool("protocol.file.cgi.policy", NULL)) return 1;
@ -313,7 +314,7 @@ execute_cgi(struct connection *conn)
script = get_uri_string(conn->uri, URI_PATH); script = get_uri_string(conn->uri, URI_PATH);
if (!script) { if (!script) {
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
goto end2; goto end2;
} }
decode_uri(script); decode_uri(script);
@ -344,13 +345,13 @@ execute_cgi(struct connection *conn)
} }
if (c_pipe(pipe_read) || c_pipe(pipe_write)) { if (c_pipe(pipe_read) || c_pipe(pipe_write)) {
state = -errno; state = connection_state_for_errno(errno);
goto end1; goto end1;
} }
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
state = -errno; state = connection_state_for_errno(errno);
goto end0; goto end0;
} }
if (!pid) { /* CGI script */ if (!pid) { /* CGI script */

View File

@ -174,34 +174,34 @@ add_dir_entries(struct directory_entry *entries, unsigned char *dirpath,
/* Generates an HTML page listing the content of @directory with the path /* Generates an HTML page listing the content of @directory with the path
* @dirpath. */ * @dirpath. */
/* Returns a connection state. S_OK if all is well. */ /* Returns a connection state. S_OK if all is well. */
static inline enum connection_state static inline struct connection_state
list_directory(struct connection *conn, unsigned char *dirpath, list_directory(struct connection *conn, unsigned char *dirpath,
struct string *page) struct string *page)
{ {
int show_hidden_files = get_opt_bool("protocol.file.show_hidden_files", int show_hidden_files = get_opt_bool("protocol.file.show_hidden_files",
NULL); NULL);
struct directory_entry *entries; struct directory_entry *entries;
enum connection_state state; struct connection_state state;
errno = 0; errno = 0;
entries = get_directory_entries(dirpath, show_hidden_files); entries = get_directory_entries(dirpath, show_hidden_files);
if (!entries) { if (!entries) {
if (errno) return -errno; if (errno) return connection_state_for_errno(errno);
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
} }
state = init_directory_listing(page, conn->uri); state = init_directory_listing(page, conn->uri);
if (state != S_OK) if (!is_in_state(state, S_OK))
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
add_dir_entries(entries, dirpath, page); add_dir_entries(entries, dirpath, page);
if (!add_to_string(page, "</pre>\n<hr/>\n</body>\n</html>\n")) { if (!add_to_string(page, "</pre>\n<hr/>\n</body>\n</html>\n")) {
done_string(page); done_string(page);
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
} }
return S_OK; return connection_state(S_OK);
} }
@ -216,13 +216,14 @@ file_protocol_handler(struct connection *connection)
{ {
unsigned char *redirect_location = NULL; unsigned char *redirect_location = NULL;
struct string page, name; struct string page, name;
enum connection_state state; struct connection_state state;
int set_dir_content_type = 0; int set_dir_content_type = 0;
if (get_cmd_opt_bool("anonymous")) { if (get_cmd_opt_bool("anonymous")) {
if (strcmp(connection->uri->string, "file:///dev/stdin") if (strcmp(connection->uri->string, "file:///dev/stdin")
|| isatty(STDIN_FILENO)) { || isatty(STDIN_FILENO)) {
abort_connection(connection, S_FILE_ANONYMOUS); abort_connection(connection,
connection_state(S_FILE_ANONYMOUS));
return; return;
} }
} }
@ -240,7 +241,7 @@ file_protocol_handler(struct connection *connection)
if (!init_string(&name) if (!init_string(&name)
|| !add_uri_to_string(&name, connection->uri, URI_PATH)) { || !add_uri_to_string(&name, connection->uri, URI_PATH)) {
done_string(&name); done_string(&name);
abort_connection(connection, S_OUT_OF_MEM); abort_connection(connection, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -255,7 +256,7 @@ file_protocol_handler(struct connection *connection)
* directory separator. */ * directory separator. */
if (name.source[0] && !dir_sep(name.source[name.length - 1])) { if (name.source[0] && !dir_sep(name.source[name.length - 1])) {
redirect_location = STRING_DIR_SEP; redirect_location = STRING_DIR_SEP;
state = S_OK; state = connection_state(S_OK);
} else { } else {
state = list_directory(connection, name.source, &page); state = list_directory(connection, name.source, &page);
set_dir_content_type = 1; set_dir_content_type = 1;
@ -269,7 +270,7 @@ file_protocol_handler(struct connection *connection)
done_string(&name); done_string(&name);
if (state == S_OK) { if (is_in_state(state, S_OK)) {
struct cache_entry *cached; struct cache_entry *cached;
/* Try to add fragment data to the connection cache if either /* Try to add fragment data to the connection cache if either
@ -277,11 +278,11 @@ file_protocol_handler(struct connection *connection)
cached = connection->cached = get_cache_entry(connection->uri); cached = connection->cached = get_cache_entry(connection->uri);
if (!connection->cached) { if (!connection->cached) {
if (!redirect_location) done_string(&page); if (!redirect_location) done_string(&page);
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
} else if (redirect_location) { } else if (redirect_location) {
if (!redirect_cache(cached, redirect_location, 1, 0)) if (!redirect_cache(cached, redirect_location, 1, 0))
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
} else { } else {
add_fragment(cached, 0, page.source, page.length); add_fragment(cached, 0, page.source, page.length);
@ -301,7 +302,7 @@ file_protocol_handler(struct connection *connection)
/* Not so gracefully handle failed memory /* Not so gracefully handle failed memory
* allocation. */ * allocation. */
if (!head) if (!head)
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
/* Setup directory listing for viewing. */ /* Setup directory listing for viewing. */
mem_free_set(&cached->head, head); mem_free_set(&cached->head, head);

View File

@ -35,13 +35,13 @@ finger_get_response(struct socket *socket, struct read_buffer *rb)
int l; int l;
if (!cached) { if (!cached) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
conn->cached = cached; conn->cached = cached;
if (socket->state == SOCKET_CLOSED) { if (socket->state == SOCKET_CLOSED) {
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
@ -53,7 +53,8 @@ finger_get_response(struct socket *socket, struct read_buffer *rb)
conn->from += l; conn->from += l;
kill_buffer_data(rb, l); kill_buffer_data(rb, l);
read_from_socket(conn->socket, rb, S_TRANS, finger_get_response); read_from_socket(conn->socket, rb, connection_state(S_TRANS),
finger_get_response);
} }
static void static void
@ -70,7 +71,8 @@ finger_send_request(struct socket *socket)
add_bytes_to_string(&req, conn->uri->user, conn->uri->userlen); add_bytes_to_string(&req, conn->uri->user, conn->uri->userlen);
} }
add_crlf_to_string(&req); add_crlf_to_string(&req);
request_from_socket(socket, req.source, req.length, S_SENT, request_from_socket(socket, req.source, req.length,
connection_state(S_SENT),
SOCKET_END_ONCLOSE, finger_get_response); SOCKET_END_ONCLOSE, finger_get_response);
done_string(&req); done_string(&req);
} }

View File

@ -61,11 +61,11 @@ struct module fsp_protocol_module = struct_module(
* *
* - If an error occurs, the child process writes "text/x-error" * - If an error occurs, the child process writes "text/x-error"
* without newline to stderr, and an error code and a newline to * without newline to stderr, and an error code and a newline to
* stdout. The error code is either from errno or a negated value * stdout. The error code is either "S" followed by errno or "I"
* from enum connection_state, e.g. -S_OUT_OF_MEM. In particular, * followed by enum connection_basic_state. In particular, EPERM
* EPERM causes the parent process to prompt for username and * causes the parent process to prompt for username and password.
* password. (In this, fsplib differs from libsmbclient, which uses * (In this, fsplib differs from libsmbclient, which uses EACCES if
* EACCES if authentication fails.) * authentication fails.)
* *
* - If the resource is a regular file, the child process writes the * - If the resource is a regular file, the child process writes the
* estimated length of the file (in bytes) and a newline to stderr, * estimated length of the file (in bytes) and a newline to stderr,
@ -90,9 +90,12 @@ struct module fsp_protocol_module = struct_module(
* stdout fails for directory listing like we do for file fetching. */ * stdout fails for directory listing like we do for file fetching. */
static void static void
fsp_error(int error) fsp_error(struct connection_state error)
{ {
printf("%d\n", error); if (is_system_error(error))
printf("S%d\n", (int) error.syserr);
else
printf("I%d\n", (int) error.basic);
fprintf(stderr, "text/x-error"); fprintf(stderr, "text/x-error");
/* In principle, this should perhaps call fsp_close_session to /* In principle, this should perhaps call fsp_close_session to
* make the server accept any key from the next client process * make the server accept any key from the next client process
@ -202,13 +205,13 @@ fsp_directory(FSP_SESSION *ses, struct uri *uri)
unsigned char dircolor[8] = ""; unsigned char dircolor[8] = "";
if (!data) if (!data)
fsp_error(-S_OUT_OF_MEM); fsp_error(connection_state(S_OUT_OF_MEM));
decode_uri(data); decode_uri(data);
if (init_directory_listing(&buf, uri) != S_OK) if (!is_in_state(init_directory_listing(&buf, uri), S_OK))
fsp_error(-S_OUT_OF_MEM); fsp_error(connection_state(S_OUT_OF_MEM));
dir = fsp_opendir(ses, data); dir = fsp_opendir(ses, data);
if (!dir) fsp_error(errno); if (!dir) fsp_error(connection_state_for_errno(errno));
fprintf(stderr, "text/html"); fprintf(stderr, "text/html");
fclose(stderr); fclose(stderr);
@ -249,8 +252,18 @@ do_fsp(struct connection *conn)
if (auth) password = auth->password; if (auth) password = auth->password;
} }
/* fsp_open_session may not set errno if getaddrinfo fails
* https://sourceforge.net/tracker/index.php?func=detail&aid=2036798&group_id=93841&atid=605738
* Try to detect this bug and use an ELinks-specific error
* code instead, so that we can display a message anyway. */
errno = 0;
ses = fsp_open_session(host, port, password); ses = fsp_open_session(host, port, password);
if (!ses) fsp_error(errno); if (!ses) {
if (errno)
fsp_error(connection_state_for_errno(errno));
else
fsp_error(connection_state(S_FSP_OPEN_SESSION_UNKN));
}
/* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS /* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS
* https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738 * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738
@ -270,7 +283,7 @@ do_fsp(struct connection *conn)
* sb.st_size really needs to be filled, but filling the rest * sb.st_size really needs to be filled, but filling the rest
* too helps viewing the data with a debugger.) */ * too helps viewing the data with a debugger.) */
memset(&sb, 0xAA, sizeof(sb)); memset(&sb, 0xAA, sizeof(sb));
if (fsp_stat(ses, data, &sb)) fsp_error(errno); if (fsp_stat(ses, data, &sb)) fsp_error(connection_state_for_errno(errno));
if (S_ISDIR(sb.st_mode)) { if (S_ISDIR(sb.st_mode)) {
fsp_directory(ses, uri); fsp_directory(ses, uri);
@ -280,7 +293,7 @@ do_fsp(struct connection *conn)
int r; int r;
if (!file) { if (!file) {
fsp_error(errno); fsp_error(connection_state_for_errno(errno));
} }
#if SIZEOF_OFF_T >= 8 #if SIZEOF_OFF_T >= 8
@ -334,7 +347,7 @@ static void
prompt_username_pw(struct connection *conn) prompt_username_pw(struct connection *conn)
{ {
add_auth_entry(conn->uri, "FSP", NULL, NULL, 0); add_auth_entry(conn->uri, "FSP", NULL, NULL, 0);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }
static void static void
@ -342,10 +355,10 @@ fsp_got_error(struct socket *socket, struct read_buffer *rb)
{ {
int len = rb->length; int len = rb->length;
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
int error; struct connection_state error;
if (len < 0) { if (len < 0) {
abort_connection(conn, -errno); abort_connection(conn, connection_state_for_errno(errno));
return; return;
} }
@ -355,20 +368,28 @@ fsp_got_error(struct socket *socket, struct read_buffer *rb)
* pipe. */ * pipe. */
assert(rb->freespace >= 1); assert(rb->freespace >= 1);
if_assert_failed { if_assert_failed {
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
return; return;
} }
rb->data[len] = '\0'; rb->data[len] = '\0';
error = atoi(rb->data); switch (rb->data[0]) {
kill_buffer_data(rb, len); case 'S':
switch (error) { error = connection_state_for_errno(atoi(rb->data + 1));
case EPERM: break;
prompt_username_pw(conn); case 'I':
error = connection_state(atoi(rb->data + 1));
break; break;
default: default:
abort_connection(conn, -error); ERROR("malformed error code: %s", rb->data);
error = connection_state(S_INTERNAL);
break; break;
} }
kill_buffer_data(rb, len);
if (is_system_error(error) && error.syserr == EPERM)
prompt_username_pw(conn);
else
abort_connection(conn, error);
} }
static void static void
@ -378,12 +399,12 @@ fsp_got_data(struct socket *socket, struct read_buffer *rb)
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
if (len < 0) { if (len < 0) {
abort_connection(conn, -errno); abort_connection(conn, connection_state_for_errno(errno));
return; return;
} }
if (!len) { if (!len) {
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
@ -394,7 +415,7 @@ fsp_got_data(struct socket *socket, struct read_buffer *rb)
conn->from += len; conn->from += len;
kill_buffer_data(rb, len); kill_buffer_data(rb, len);
read_from_socket(socket, rb, S_TRANS, fsp_got_data); read_from_socket(socket, rb, connection_state(S_TRANS), fsp_got_data);
} }
static void static void
@ -413,7 +434,7 @@ fsp_got_header(struct socket *socket, struct read_buffer *rb)
* and assume abort_connection will do them?) */ * and assume abort_connection will do them?) */
close_socket(socket); close_socket(socket);
close_socket(conn->data_socket); close_socket(conn->data_socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
socket->state = SOCKET_END_ONCLOSE; socket->state = SOCKET_END_ONCLOSE;
@ -436,7 +457,7 @@ fsp_got_header(struct socket *socket, struct read_buffer *rb)
/* avoid read from socket error */ /* avoid read from socket error */
if (!conn->est_length) { if (!conn->est_length) {
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
} }
@ -451,15 +472,17 @@ fsp_got_header(struct socket *socket, struct read_buffer *rb)
if (!buf) { if (!buf) {
close_socket(socket); close_socket(socket);
close_socket(conn->data_socket); close_socket(conn->data_socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
if (error) { if (error) {
mem_free_set(&conn->cached->content_type, stracpy("text/html")); mem_free_set(&conn->cached->content_type, stracpy("text/html"));
read_from_socket(conn->data_socket, buf, S_CONN, fsp_got_error); read_from_socket(conn->data_socket, buf,
connection_state(S_CONN), fsp_got_error);
} else { } else {
read_from_socket(conn->data_socket, buf, S_CONN, fsp_got_data); read_from_socket(conn->data_socket, buf,
connection_state(S_CONN), fsp_got_data);
} }
} }
@ -478,7 +501,7 @@ fsp_protocol_handler(struct connection *conn)
if (fsp_pipe[1] >= 0) close(fsp_pipe[1]); if (fsp_pipe[1] >= 0) close(fsp_pipe[1]);
if (header_pipe[0] >= 0) close(header_pipe[0]); if (header_pipe[0] >= 0) close(header_pipe[0]);
if (header_pipe[1] >= 0) close(header_pipe[1]); if (header_pipe[1] >= 0) close(header_pipe[1]);
abort_connection(conn, -s_errno); abort_connection(conn, connection_state_for_errno(s_errno));
return; return;
} }
conn->from = 0; conn->from = 0;
@ -493,7 +516,7 @@ fsp_protocol_handler(struct connection *conn)
close(fsp_pipe[1]); close(fsp_pipe[1]);
close(header_pipe[0]); close(header_pipe[0]);
close(header_pipe[1]); close(header_pipe[1]);
retry_connection(conn, -s_errno); retry_connection(conn, connection_state_for_errno(s_errno));
return; return;
} }
@ -531,9 +554,10 @@ fsp_protocol_handler(struct connection *conn)
if (!buf2) { if (!buf2) {
close_socket(conn->data_socket); close_socket(conn->data_socket);
close_socket(conn->socket); close_socket(conn->socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
read_from_socket(conn->socket, buf2, S_CONN, fsp_got_header); read_from_socket(conn->socket, buf2,
connection_state(S_CONN), fsp_got_header);
} }
} }

View File

@ -119,7 +119,7 @@ struct ftp_connection_info {
/* Prototypes */ /* Prototypes */
static void ftp_login(struct socket *); static void ftp_login(struct socket *);
static void ftp_send_retr_req(struct connection *, int); static void ftp_send_retr_req(struct connection *, struct connection_state);
static void ftp_got_info(struct socket *, struct read_buffer *); static void ftp_got_info(struct socket *, struct read_buffer *);
static void ftp_got_user_info(struct socket *, struct read_buffer *); static void ftp_got_user_info(struct socket *, struct read_buffer *);
static void ftp_pass(struct connection *); static void ftp_pass(struct connection *);
@ -127,7 +127,7 @@ static void ftp_pass_info(struct socket *, struct read_buffer *);
static void ftp_retr_file(struct socket *, struct read_buffer *); static void ftp_retr_file(struct socket *, struct read_buffer *);
static void ftp_got_final_response(struct socket *, struct read_buffer *); static void ftp_got_final_response(struct socket *, struct read_buffer *);
static void got_something_from_data_connection(struct connection *); static void got_something_from_data_connection(struct connection *);
static void ftp_end_request(struct connection *, enum connection_state); static void ftp_end_request(struct connection *, struct connection_state);
static struct ftp_connection_info *add_file_cmd_to_str(struct connection *); static struct ftp_connection_info *add_file_cmd_to_str(struct connection *);
static void ftp_data_accept(struct connection *conn); static void ftp_data_accept(struct connection *conn);
@ -276,13 +276,14 @@ ftp_protocol_handler(struct connection *conn)
conn->cache_mode >= CACHE_MODE_FORCE_RELOAD); conn->cache_mode >= CACHE_MODE_FORCE_RELOAD);
} else { } else {
ftp_send_retr_req(conn, S_SENT); ftp_send_retr_req(conn, connection_state(S_SENT));
} }
} }
/* Send command, set connection state and free cmd string. */ /* Send command, set connection state and free cmd string. */
static void static void
send_cmd(struct connection *conn, struct string *cmd, void *callback, int state) send_cmd(struct connection *conn, struct string *cmd, void *callback,
struct connection_state state)
{ {
request_from_socket(conn->socket, cmd->source, cmd->length, state, request_from_socket(conn->socket, cmd->source, cmd->length, state,
SOCKET_RETRY_ONCLOSE, callback); SOCKET_RETRY_ONCLOSE, callback);
@ -308,20 +309,20 @@ prompt_username_pw(struct connection *conn)
if (!conn->cached) { if (!conn->cached) {
conn->cached = get_cache_entry(conn->uri); conn->cached = get_cache_entry(conn->uri);
if (!conn->cached) { if (!conn->cached) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
} }
mem_free_set(&conn->cached->content_type, stracpy("text/html")); mem_free_set(&conn->cached->content_type, stracpy("text/html"));
if (!conn->cached->content_type) { if (!conn->cached->content_type) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
add_auth_entry(conn->uri, "FTP Login", NULL, NULL, 0); add_auth_entry(conn->uri, "FTP Login", NULL, NULL, 0);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }
/* Send USER command. */ /* Send USER command. */
@ -335,7 +336,7 @@ ftp_login(struct socket *socket)
auth = find_auth(conn->uri); auth = find_auth(conn->uri);
if (!init_string(&cmd)) { if (!init_string(&cmd)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -353,7 +354,7 @@ ftp_login(struct socket *socket)
} }
add_crlf_to_string(&cmd); add_crlf_to_string(&cmd);
send_cmd(conn, &cmd, (void *) ftp_got_info, S_SENT); send_cmd(conn, &cmd, (void *) ftp_got_info, connection_state(S_SENT));
} }
/* Parse connection response. */ /* Parse connection response. */
@ -364,7 +365,7 @@ ftp_got_info(struct socket *socket, struct read_buffer *rb)
int response = get_ftp_response(conn, rb, 0, NULL); int response = get_ftp_response(conn, rb, 0, NULL);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
@ -380,7 +381,7 @@ ftp_got_info(struct socket *socket, struct read_buffer *rb)
if (response != 220) { if (response != 220) {
/* TODO? Retry in case of ... ?? */ /* TODO? Retry in case of ... ?? */
retry_connection(conn, S_FTP_UNAVAIL); retry_connection(conn, connection_state(S_FTP_UNAVAIL));
return; return;
} }
@ -396,7 +397,7 @@ ftp_got_user_info(struct socket *socket, struct read_buffer *rb)
int response = get_ftp_response(conn, rb, 0, NULL); int response = get_ftp_response(conn, rb, 0, NULL);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
@ -426,12 +427,12 @@ ftp_got_user_info(struct socket *socket, struct read_buffer *rb)
* non-RFC compliant servers may return even something other than 421. * non-RFC compliant servers may return even something other than 421.
* --Zas */ * --Zas */
if (response >= 400) { if (response >= 400) {
abort_connection(conn, S_FTP_UNAVAIL); abort_connection(conn, connection_state(S_FTP_UNAVAIL));
return; return;
} }
if (response == 230) { if (response == 230) {
ftp_send_retr_req(conn, S_GETH); ftp_send_retr_req(conn, connection_state(S_GETH));
return; return;
} }
@ -448,7 +449,7 @@ ftp_pass(struct connection *conn)
auth = find_auth(conn->uri); auth = find_auth(conn->uri);
if (!init_string(&cmd)) { if (!init_string(&cmd)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -471,7 +472,7 @@ ftp_pass(struct connection *conn)
} }
add_crlf_to_string(&cmd); add_crlf_to_string(&cmd);
send_cmd(conn, &cmd, (void *) ftp_pass_info, S_LOGIN); send_cmd(conn, &cmd, (void *) ftp_pass_info, connection_state(S_LOGIN));
} }
/* Parse PASS command response. */ /* Parse PASS command response. */
@ -482,12 +483,13 @@ ftp_pass_info(struct socket *socket, struct read_buffer *rb)
int response = get_ftp_response(conn, rb, 0, NULL); int response = get_ftp_response(conn, rb, 0, NULL);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
if (!response) { if (!response) {
read_from_socket(conn->socket, rb, S_LOGIN, ftp_pass_info); read_from_socket(conn->socket, rb, connection_state(S_LOGIN),
ftp_pass_info);
return; return;
} }
@ -509,11 +511,11 @@ ftp_pass_info(struct socket *socket, struct read_buffer *rb)
} }
if (response >= 400) { if (response >= 400) {
abort_connection(conn, S_FTP_UNAVAIL); abort_connection(conn, connection_state(S_FTP_UNAVAIL));
return; return;
} }
ftp_send_retr_req(conn, S_GETH); ftp_send_retr_req(conn, connection_state(S_GETH));
} }
/* Construct PORT command. */ /* Construct PORT command. */
@ -674,14 +676,14 @@ add_file_cmd_to_str(struct connection *conn)
if (!conn->uri->data) { if (!conn->uri->data) {
INTERNAL("conn->uri->data empty"); INTERNAL("conn->uri->data empty");
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
goto ret; goto ret;
} }
assert(conn->info == NULL); assert(conn->info == NULL);
assert(conn->done == NULL); assert(conn->done == NULL);
if_assert_failed { if_assert_failed {
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
goto ret; goto ret;
} }
@ -692,7 +694,7 @@ add_file_cmd_to_str(struct connection *conn)
* risky. */ * risky. */
ftp = mem_calloc(1, sizeof(*ftp)); ftp = mem_calloc(1, sizeof(*ftp));
if (!ftp) { if (!ftp) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
@ -702,24 +704,24 @@ add_file_cmd_to_str(struct connection *conn)
if (!init_string(&command) if (!init_string(&command)
|| !init_string(&ftp_data_command) || !init_string(&ftp_data_command)
|| !init_string(&pathname)) { || !init_string(&pathname)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
if (!get_ftp_data_socket(conn, &ftp_data_command)) { if (!get_ftp_data_socket(conn, &ftp_data_command)) {
INTERNAL("Ftp data socket failure"); INTERNAL("Ftp data socket failure");
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
goto ret; goto ret;
} }
if (!add_uri_to_string(&pathname, conn->uri, URI_PATH)) { if (!add_uri_to_string(&pathname, conn->uri, URI_PATH)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
decode_uri_string(&pathname); decode_uri_string(&pathname);
if (!is_ftp_pathname_safe(&pathname)) { if (!is_ftp_pathname_safe(&pathname)) {
abort_connection(conn, S_BAD_URL); abort_connection(conn, connection_state(S_BAD_URL));
goto ret; goto ret;
} }
@ -741,7 +743,7 @@ add_file_cmd_to_str(struct connection *conn)
|| !add_to_string(&command, "LIST") || !add_to_string(&command, "LIST")
|| !add_crlf_to_string(&command)) { || !add_crlf_to_string(&command)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
@ -757,7 +759,7 @@ add_file_cmd_to_str(struct connection *conn)
|| !add_crlf_to_string(&command) || !add_crlf_to_string(&command)
|| !add_string_to_string(&command, &ftp_data_command)) { || !add_string_to_string(&command, &ftp_data_command)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
@ -769,7 +771,7 @@ add_file_cmd_to_str(struct connection *conn)
if (!add_to_string(&command, "REST ") if (!add_to_string(&command, "REST ")
|| !add_long_to_string(&command, offset) || !add_long_to_string(&command, offset)
|| !add_crlf_to_string(&command)) { || !add_crlf_to_string(&command)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
@ -780,7 +782,7 @@ add_file_cmd_to_str(struct connection *conn)
if (!add_to_string(&command, "RETR ") if (!add_to_string(&command, "RETR ")
|| !add_string_to_string(&command, &pathname) || !add_string_to_string(&command, &pathname)
|| !add_crlf_to_string(&command)) { || !add_crlf_to_string(&command)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
} }
@ -790,7 +792,7 @@ add_file_cmd_to_str(struct connection *conn)
/* 1 byte is already reserved for cmd_buffer in struct ftp_connection_info. */ /* 1 byte is already reserved for cmd_buffer in struct ftp_connection_info. */
ftp = mem_realloc(ftp, sizeof(*ftp) + command.length); ftp = mem_realloc(ftp, sizeof(*ftp) + command.length);
if (!ftp) { if (!ftp) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
goto ret; goto ret;
} }
conn->info = ftp; /* in case mem_realloc moved the buffer */ conn->info = ftp; /* in case mem_realloc moved the buffer */
@ -825,12 +827,12 @@ send_it_line_by_line(struct connection *conn, struct string *cmd)
/* Send commands to retrieve file or directory. */ /* Send commands to retrieve file or directory. */
static void static void
ftp_send_retr_req(struct connection *conn, int state) ftp_send_retr_req(struct connection *conn, struct connection_state state)
{ {
struct string cmd; struct string cmd;
if (!init_string(&cmd)) { if (!init_string(&cmd)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -925,13 +927,13 @@ ftp_data_connect(struct connection *conn, int pf, struct sockaddr_storage *sa,
if (conn->data_socket->fd != -1) { if (conn->data_socket->fd != -1) {
/* The server maliciously sent multiple 227 or 229 /* The server maliciously sent multiple 227 or 229
* responses. Do not leak the previous data_socket. */ * responses. Do not leak the previous data_socket. */
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return -1; return -1;
} }
fd = socket(pf, SOCK_STREAM, 0); fd = socket(pf, SOCK_STREAM, 0);
if (fd < 0 || set_nonblocking_fd(fd) < 0) { if (fd < 0 || set_nonblocking_fd(fd) < 0) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return -1; return -1;
} }
@ -956,12 +958,14 @@ ftp_retr_file(struct socket *socket, struct read_buffer *rb)
response = get_ftp_response(conn, rb, 0, &sa); response = get_ftp_response(conn, rb, 0, &sa);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
if (!response) { if (!response) {
read_from_socket(conn->socket, rb, S_GETH, ftp_retr_file); read_from_socket(conn->socket, rb,
connection_state(S_GETH),
ftp_retr_file);
return; return;
} }
@ -986,7 +990,8 @@ ftp_retr_file(struct socket *socket, struct read_buffer *rb)
case 2: /* PORT */ case 2: /* PORT */
if (response >= 400) { if (response >= 400) {
abort_connection(conn, S_FTP_PORT); abort_connection(conn,
connection_state(S_FTP_PORT));
return; return;
} }
break; break;
@ -995,7 +1000,7 @@ ftp_retr_file(struct socket *socket, struct read_buffer *rb)
if (response >= 400) { if (response >= 400) {
if (ftp->dir) { if (ftp->dir) {
abort_connection(conn, abort_connection(conn,
S_FTP_NO_FILE); connection_state(S_FTP_NO_FILE));
return; return;
} }
conn->from = 0; conn->from = 0;
@ -1019,19 +1024,20 @@ ftp_retr_file(struct socket *socket, struct read_buffer *rb)
INTERNAL("WHAT???"); INTERNAL("WHAT???");
} }
ftp_send_retr_req(conn, S_GETH); ftp_send_retr_req(conn, connection_state(S_GETH));
return; return;
} }
response = get_ftp_response(conn, rb, 2, NULL); response = get_ftp_response(conn, rb, 2, NULL);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
if (!response) { if (!response) {
read_from_socket(conn->socket, rb, S_GETH, ftp_retr_file); read_from_socket(conn->socket, rb, connection_state(S_GETH),
ftp_retr_file);
return; return;
} }
@ -1066,7 +1072,7 @@ ftp_retr_file(struct socket *socket, struct read_buffer *rb)
* get_ftp_data_socket would have created the * get_ftp_data_socket would have created the
* data_socket without waiting for anything from the * data_socket without waiting for anything from the
* server. */ * server. */
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
set_handlers(conn->data_socket->fd, (select_handler_T) ftp_data_accept, set_handlers(conn->data_socket->fd, (select_handler_T) ftp_data_accept,
@ -1084,13 +1090,13 @@ ftp_got_final_response(struct socket *socket, struct read_buffer *rb)
int response = get_ftp_response(conn, rb, 0, NULL); int response = get_ftp_response(conn, rb, 0, NULL);
if (response == -1) { if (response == -1) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
if (!response) { if (!response) {
enum connection_state state = conn->state != S_TRANS struct connection_state state = !is_in_state(conn->state, S_TRANS)
? S_GETH : conn->state; ? connection_state(S_GETH) : conn->state;
read_from_socket(conn->socket, rb, state, ftp_got_final_response); read_from_socket(conn->socket, rb, state, ftp_got_final_response);
return; return;
@ -1105,25 +1111,25 @@ ftp_got_final_response(struct socket *socket, struct read_buffer *rb)
if (!conn->cached if (!conn->cached
|| !redirect_cache(conn->cached, "/", 1, 0)) { || !redirect_cache(conn->cached, "/", 1, 0)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
if (response >= 400) { if (response >= 400) {
abort_connection(conn, S_FTP_FILE_ERROR); abort_connection(conn, connection_state(S_FTP_FILE_ERROR));
return; return;
} }
if (ftp->conn_state == 2) { if (ftp->conn_state == 2) {
ftp_end_request(conn, S_OK); ftp_end_request(conn, connection_state(S_OK));
} else { } else {
ftp->conn_state = 1; ftp->conn_state = 1;
if (conn->state != S_TRANS) if (!is_in_state(conn->state, S_TRANS))
set_connection_state(conn, S_GETH); set_connection_state(conn, connection_state(S_GETH));
} }
} }
@ -1368,7 +1374,7 @@ ftp_data_accept(struct connection *conn)
} else { } else {
newsock = accept(conn->data_socket->fd, NULL, NULL); newsock = accept(conn->data_socket->fd, NULL, NULL);
if (newsock < 0) { if (newsock < 0) {
retry_connection(conn, -errno); retry_connection(conn, connection_state_for_errno(errno));
return; return;
} }
close(conn->data_socket->fd); close(conn->data_socket->fd);
@ -1404,7 +1410,7 @@ got_something_from_data_connection(struct connection *conn)
if (!conn->cached) conn->cached = get_cache_entry(conn->uri); if (!conn->cached) conn->cached = get_cache_entry(conn->uri);
if (!conn->cached) { if (!conn->cached) {
out_of_mem: out_of_mem:
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -1421,15 +1427,15 @@ out_of_mem:
if (ftp->dir && !conn->from) { if (ftp->dir && !conn->from) {
struct string string; struct string string;
enum connection_state state; struct connection_state state;
if (!conn->uri->data) { if (!conn->uri->data) {
abort_connection(conn, S_FTP_ERROR); abort_connection(conn, connection_state(S_FTP_ERROR));
return; return;
} }
state = init_directory_listing(&string, conn->uri); state = init_directory_listing(&string, conn->uri);
if (state != S_OK) { if (!is_in_state(state, S_OK)) {
abort_connection(conn, state); abort_connection(conn, state);
return; return;
} }
@ -1452,7 +1458,7 @@ out_of_mem:
len = safe_read(conn->data_socket->fd, ftp->ftp_buffer + ftp->buf_pos, len = safe_read(conn->data_socket->fd, ftp->ftp_buffer + ftp->buf_pos,
FTP_BUF_SIZE - ftp->buf_pos); FTP_BUF_SIZE - ftp->buf_pos);
if (len < 0) { if (len < 0) {
retry_connection(conn, -errno); retry_connection(conn, connection_state_for_errno(errno));
return; return;
} }
@ -1484,7 +1490,7 @@ out_of_mem:
} }
set_connection_state(conn, S_TRANS); set_connection_state(conn, connection_state(S_TRANS));
return; return;
} }
@ -1502,17 +1508,17 @@ out_of_mem:
close_socket(conn->data_socket); close_socket(conn->data_socket);
if (ftp->conn_state == 1) { if (ftp->conn_state == 1) {
ftp_end_request(conn, S_OK); ftp_end_request(conn, connection_state(S_OK));
} else { } else {
ftp->conn_state = 2; ftp->conn_state = 2;
set_connection_state(conn, S_TRANS); set_connection_state(conn, connection_state(S_TRANS));
} }
} }
static void static void
ftp_end_request(struct connection *conn, enum connection_state state) ftp_end_request(struct connection *conn, struct connection_state state)
{ {
if (state == S_OK && conn->cached) { if (is_in_state(state, S_OK) && conn->cached) {
normalize_cache_entry(conn->cached, conn->from); normalize_cache_entry(conn->cached, conn->from);
} }

View File

@ -197,9 +197,9 @@ add_uri_decoded(struct string *command, unsigned char *string, int length,
command->length = strlen(command->source); command->length = strlen(command->source);
} }
static enum connection_state init_gopher_index_cache_entry(struct connection *conn); static struct connection_state init_gopher_index_cache_entry(struct connection *conn);
static enum connection_state static struct connection_state
add_gopher_command(struct connection *conn, struct string *command, add_gopher_command(struct connection *conn, struct string *command,
enum gopher_entity entity, enum gopher_entity entity,
unsigned char *selector, int selectorlen) unsigned char *selector, int selectorlen)
@ -269,11 +269,11 @@ add_gopher_command(struct connection *conn, struct string *command,
return S_CONN; return S_CONN;
} }
static enum connection_state static struct connection_state
init_gopher_connection_info(struct connection *conn) init_gopher_connection_info(struct connection *conn)
{ {
struct gopher_connection_info *gopher; struct gopher_connection_info *gopher;
enum connection_state state; struct connection_state state;
struct string command; struct string command;
enum gopher_entity entity = DEFAULT_GOPHER_ENTITY; enum gopher_entity entity = DEFAULT_GOPHER_ENTITY;
unsigned char *selector = conn->uri->data; unsigned char *selector = conn->uri->data;
@ -576,15 +576,15 @@ check_gopher_last_line(unsigned char *line, unsigned char *end)
} }
/* Parse a Gopher Menu document */ /* Parse a Gopher Menu document */
static enum connection_state static struct connection_state
read_gopher_directory_data(struct connection *conn, struct read_buffer *rb) read_gopher_directory_data(struct connection *conn, struct read_buffer *rb)
{ {
enum connection_state state = S_TRANS; struct connection_state state = connection_state(S_TRANS);
struct string buffer; struct string buffer;
unsigned char *end; unsigned char *end;
if (conn->from == 0) { if (conn->from == 0) {
enum connection_state state; struct connection_state state;
state = init_directory_listing(&buffer, conn->uri); state = init_directory_listing(&buffer, conn->uri);
if (state != S_OK) if (state != S_OK)
@ -645,7 +645,7 @@ init_gopher_cache_entry(struct connection *conn)
} }
/* Display a Gopher Index document. */ /* Display a Gopher Index document. */
static enum connection_state static struct connection_state
init_gopher_index_cache_entry(struct connection *conn) init_gopher_index_cache_entry(struct connection *conn)
{ {
unsigned char *where; unsigned char *where;
@ -695,7 +695,7 @@ read_gopher_response_data(struct socket *socket, struct read_buffer *rb)
{ {
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
struct gopher_connection_info *gopher = conn->info; struct gopher_connection_info *gopher = conn->info;
enum connection_state state = S_TRANS; struct connection_state state = S_TRANS;
assert(gopher && gopher->entity); assert(gopher && gopher->entity);
@ -774,7 +774,7 @@ void
gopher_protocol_handler(struct connection *conn) gopher_protocol_handler(struct connection *conn)
{ {
struct uri *uri = conn->uri; struct uri *uri = conn->uri;
enum connection_state state = S_CONN; struct connection_state state = S_CONN;
switch (get_uri_port(uri)) { switch (get_uri_port(uri)) {
case 105: case 105:

View File

@ -8,6 +8,7 @@ enum blacklist_flags {
SERVER_BLACKLIST_NONE = 0, SERVER_BLACKLIST_NONE = 0,
SERVER_BLACKLIST_HTTP10 = 1, SERVER_BLACKLIST_HTTP10 = 1,
SERVER_BLACKLIST_NO_CHARSET = 2, SERVER_BLACKLIST_NO_CHARSET = 2,
SERVER_BLACKLIST_NO_TLS = 4,
}; };
void add_blacklist_entry(struct uri *, enum blacklist_flags); void add_blacklist_entry(struct uri *, enum blacklist_flags);

View File

@ -461,7 +461,7 @@ check_http_server_bugs(struct uri *uri, struct http_connection_info *http,
} }
static void static void
http_end_request(struct connection *conn, enum connection_state state, http_end_request(struct connection *conn, struct connection_state state,
int notrunc) int notrunc)
{ {
struct http_connection_info *http; struct http_connection_info *http;
@ -478,7 +478,7 @@ http_end_request(struct connection *conn, enum connection_state state,
&& (!conn->socket->ssl) /* We won't keep alive ssl connections */ && (!conn->socket->ssl) /* We won't keep alive ssl connections */
&& (!get_opt_bool("protocol.http.bugs.post_no_keepalive", NULL) && (!get_opt_bool("protocol.http.bugs.post_no_keepalive", NULL)
|| !conn->uri->post)) { || !conn->uri->post)) {
if (state == S_OK && conn->cached) if (is_in_state(state, S_OK) && conn->cached)
normalize_cache_entry(conn->cached, !notrunc ? conn->from : -1); normalize_cache_entry(conn->cached, !notrunc ? conn->from : -1);
set_connection_state(conn, state); set_connection_state(conn, state);
add_keepalive_connection(conn, HTTP_KEEPALIVE_TIMEOUT, NULL); add_keepalive_connection(conn, HTTP_KEEPALIVE_TIMEOUT, NULL);
@ -533,7 +533,7 @@ init_http_connection_info(struct connection *conn, int major, int minor, int clo
http = mem_calloc(1, sizeof(*http)); http = mem_calloc(1, sizeof(*http));
if (!http) { if (!http) {
http_end_request(conn, S_OUT_OF_MEM, 0); http_end_request(conn, connection_state(S_OUT_OF_MEM), 0);
return NULL; return NULL;
} }
@ -602,13 +602,13 @@ send_more_post_data(struct socket *socket)
struct http_connection_info *http = conn->info; struct http_connection_info *http = conn->info;
unsigned char buffer[POST_BUFFER_SIZE]; unsigned char buffer[POST_BUFFER_SIZE];
int got; int got;
enum connection_state error; struct connection_state error;
got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error); got = read_http_post(&http->post, buffer, POST_BUFFER_SIZE, &error);
if (got < 0) { if (got < 0) {
http_end_request(conn, error, 0); http_end_request(conn, error, 0);
} else if (got > 0) { } else if (got > 0) {
write_to_socket(socket, buffer, got, S_TRANS, write_to_socket(socket, buffer, got, connection_state(S_TRANS),
send_more_post_data); send_more_post_data);
} else { /* got == 0, meaning end of data */ } else { /* got == 0, meaning end of data */
/* Can't use request_from_socket() because there's no /* Can't use request_from_socket() because there's no
@ -617,9 +617,11 @@ send_more_post_data(struct socket *socket)
socket->state = SOCKET_END_ONCLOSE; socket->state = SOCKET_END_ONCLOSE;
if (rb) if (rb)
read_from_socket(socket, rb, S_SENT, http_got_header); read_from_socket(socket, rb, connection_state(S_SENT),
http_got_header);
else else
http_end_request(conn, S_OUT_OF_MEM, 0); http_end_request(conn, connection_state(S_OUT_OF_MEM),
0);
} }
} }
@ -640,7 +642,7 @@ http_send_header(struct socket *socket)
/* Sanity check for a host */ /* Sanity check for a host */
if (!uri || !uri->host || !*uri->host || !uri->hostlen) { if (!uri || !uri->host || !*uri->host || !uri->hostlen) {
http_end_request(conn, S_BAD_URL, 0); http_end_request(conn, connection_state(S_BAD_URL), 0);
return; return;
} }
@ -648,7 +650,7 @@ http_send_header(struct socket *socket)
if (!http) return; if (!http) return;
if (!init_string(&header)) { if (!init_string(&header)) {
http_end_request(conn, S_OUT_OF_MEM, 0); http_end_request(conn, connection_state(S_OUT_OF_MEM), 0);
return; return;
} }
@ -970,7 +972,7 @@ http_send_header(struct socket *socket)
* as set by get_form_uri(). This '\n' is dropped if any * as set by get_form_uri(). This '\n' is dropped if any
* and replaced by correct '\r\n' termination here. */ * and replaced by correct '\r\n' termination here. */
unsigned char *postend = strchr(uri->post, '\n'); unsigned char *postend = strchr(uri->post, '\n');
enum connection_state error; struct connection_state error;
if (postend) { if (postend) {
add_to_string(&header, "Content-Type: "); add_to_string(&header, "Content-Type: ");
@ -1015,10 +1017,12 @@ http_send_header(struct socket *socket)
socket->state = SOCKET_END_ONCLOSE; socket->state = SOCKET_END_ONCLOSE;
if (!conn->http_upload_progress && http->post.file_count) if (!conn->http_upload_progress && http->post.file_count)
conn->http_upload_progress = init_progress(0); conn->http_upload_progress = init_progress(0);
write_to_socket(socket, header.source, header.length, S_TRANS, write_to_socket(socket, header.source, header.length,
connection_state(S_TRANS),
send_more_post_data); send_more_post_data);
} else } else
request_from_socket(socket, header.source, header.length, S_SENT, request_from_socket(socket, header.source, header.length,
connection_state(S_SENT),
SOCKET_END_ONCLOSE, http_got_header); SOCKET_END_ONCLOSE, http_got_header);
done_string(&header); done_string(&header);
} }
@ -1170,7 +1174,8 @@ static void
read_more_http_data(struct connection *conn, struct read_buffer *rb, read_more_http_data(struct connection *conn, struct read_buffer *rb,
int already_got_anything) int already_got_anything)
{ {
enum connection_state state = already_got_anything ? S_TRANS : conn->state; struct connection_state state = already_got_anything
? connection_state(S_TRANS) : conn->state;
read_from_socket(conn->socket, rb, state, read_http_data); read_from_socket(conn->socket, rb, state, read_http_data);
} }
@ -1195,7 +1200,7 @@ read_http_data_done(struct connection *conn)
} }
} }
http_end_request(conn, S_OK, 0); http_end_request(conn, connection_state(S_OK), 0);
} }
/* Returns: /* Returns:
@ -1387,7 +1392,7 @@ read_http_data(struct socket *socket, struct read_buffer *rb)
break; break;
default: default:
assertm(ret == -1, "Unexpected return value: %d", ret); assertm(ret == -1, "Unexpected return value: %d", ret);
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
} }
} }
@ -1494,7 +1499,9 @@ http_got_header(struct socket *socket, struct read_buffer *rb)
unsigned char *d; unsigned char *d;
struct uri *uri = conn->proxied_uri; /* Set to the real uri */ struct uri *uri = conn->proxied_uri; /* Set to the real uri */
struct http_version version = { 0, 9 }; struct http_version version = { 0, 9 };
enum connection_state state = (conn->state != S_PROC ? S_GETH : S_PROC); struct connection_state state = (!is_in_state(conn->state, S_PROC)
? connection_state(S_GETH)
: connection_state(S_PROC));
int a, h = 200; int a, h = 200;
int cf; int cf;
@ -1507,7 +1514,7 @@ http_got_header(struct socket *socket, struct read_buffer *rb)
conn->tries = -1; conn->tries = -1;
} }
} }
retry_connection(conn, S_CANT_READ); retry_connection(conn, connection_state(S_CANT_READ));
return; return;
} }
socket->state = SOCKET_RETRY_ONCLOSE; socket->state = SOCKET_RETRY_ONCLOSE;
@ -1515,7 +1522,7 @@ http_got_header(struct socket *socket, struct read_buffer *rb)
again: again:
a = get_header(rb); a = get_header(rb);
if (a == -1) { if (a == -1) {
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
return; return;
} }
if (!a) { if (!a) {
@ -1528,7 +1535,7 @@ again:
if (a == -2) a = 0; if (a == -2) a = 0;
if ((a && get_http_code(rb, &h, &version)) if ((a && get_http_code(rb, &h, &version))
|| h == 101) { || h == 101) {
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
return; return;
} }
@ -1541,13 +1548,13 @@ again:
head = (a ? memacpy(rb->data, a) head = (a ? memacpy(rb->data, a)
: stracpy("\r\nContent-Type: text/html\r\n")); : stracpy("\r\nContent-Type: text/html\r\n"));
if (!head) { if (!head) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
if (check_http_server_bugs(uri, http, head)) { if (check_http_server_bugs(uri, http, head)) {
mem_free(head); mem_free(head);
retry_connection(conn, S_RESTART); retry_connection(conn, connection_state(S_RESTART));
return; return;
} }
@ -1566,7 +1573,7 @@ again:
if (h2 >= 100 && h2 < 600) h = h2; if (h2 >= 100 && h2 < 600) h = h2;
if (h == 101) { if (h == 101) {
mem_free(head); mem_free(head);
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
return; return;
} }
} }
@ -1584,23 +1591,23 @@ again:
if (h == 100) { if (h == 100) {
mem_free(head); mem_free(head);
state = S_PROC; state = connection_state(S_PROC);
kill_buffer_data(rb, a); kill_buffer_data(rb, a);
goto again; goto again;
} }
if (h < 200) { if (h < 200) {
mem_free(head); mem_free(head);
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
return; return;
} }
if (h == 304) { if (h == 304) {
mem_free(head); mem_free(head);
http_end_request(conn, S_OK, 1); http_end_request(conn, connection_state(S_OK), 1);
return; return;
} }
if (h == 204) { if (h == 204) {
mem_free(head); mem_free(head);
http_end_request(conn, S_HTTP_204, 0); http_end_request(conn, connection_state(S_HTTP_204), 0);
return; return;
} }
if (h == 200 && connection_is_https_proxy(conn) && !conn->socket->ssl) { if (h == 200 && connection_is_https_proxy(conn) && !conn->socket->ssl) {
@ -1609,7 +1616,7 @@ again:
socket->need_ssl = 1; socket->need_ssl = 1;
complete_connect_socket(socket, uri, http_send_header); complete_connect_socket(socket, uri, http_send_header);
#else #else
abort_connection(conn, S_SSL_ERROR); abort_connection(conn, connection_state(S_SSL_ERROR));
#endif #endif
return; return;
} }
@ -1617,7 +1624,7 @@ again:
conn->cached = get_cache_entry(conn->uri); conn->cached = get_cache_entry(conn->uri);
if (!conn->cached) { if (!conn->cached) {
mem_free(head); mem_free(head);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
conn->cached->cgi = conn->cgi; conn->cached->cgi = conn->cgi;
@ -1711,7 +1718,7 @@ again:
if (h == 401) { if (h == 401) {
if (check_http_authentication(conn, uri, if (check_http_authentication(conn, uri,
conn->cached->head, "WWW-Authenticate")) { conn->cached->head, "WWW-Authenticate")) {
retry_connection(conn, S_RESTART); retry_connection(conn, connection_state(S_RESTART));
return; return;
} }
@ -1784,7 +1791,7 @@ again:
if ((conn->progress->start <= 0 && conn->from > cf) || conn->from < 0) { if ((conn->progress->start <= 0 && conn->from > cf) || conn->from < 0) {
/* We don't want this if conn->progress.start because then conn->from will /* We don't want this if conn->progress.start because then conn->from will
* be probably value of conn->progress.start, while cf is 0. */ * be probably value of conn->progress.start, while cf is 0. */
abort_connection(conn, S_HTTP_ERROR); abort_connection(conn, connection_state(S_HTTP_ERROR));
return; return;
} }
@ -1851,7 +1858,7 @@ again:
if (conn->from) { if (conn->from) {
conn->from = 0; conn->from = 0;
mem_free(d); mem_free(d);
retry_connection(conn, S_MODIFIED); retry_connection(conn, connection_state(S_MODIFIED));
return; return;
} }
} }
@ -1883,7 +1890,7 @@ again:
if (conn->from) { if (conn->from) {
conn->from = 0; conn->from = 0;
mem_free(d); mem_free(d);
retry_connection(conn, S_MODIFIED); retry_connection(conn, connection_state(S_MODIFIED));
return; return;
} }
} }

View File

@ -88,7 +88,7 @@ done_http_post(struct http_post *http_post)
* @relates http_post */ * @relates http_post */
int int
open_http_post(struct http_post *http_post, const unsigned char *post_data, open_http_post(struct http_post *http_post, const unsigned char *post_data,
enum connection_state *error) struct connection_state *error)
{ {
off_t size = 0; off_t size = 0;
size_t length = strlen(post_data); size_t length = strlen(post_data);
@ -111,13 +111,13 @@ open_http_post(struct http_post *http_post, const unsigned char *post_data,
filename = memacpy(begin + 1, end - begin - 1); /* adds '\0' */ filename = memacpy(begin + 1, end - begin - 1); /* adds '\0' */
if (!filename) { if (!filename) {
done_http_post(http_post); done_http_post(http_post);
*error = S_OUT_OF_MEM; *error = connection_state(S_OUT_OF_MEM);
return 0; return 0;
} }
decode_uri(filename); decode_uri(filename);
res = stat(filename, &sb); res = stat(filename, &sb);
if (res) { if (res) {
*error = -errno; *error = connection_state_for_errno(errno);
done_http_post(http_post); done_http_post(http_post);
return 0; return 0;
} }
@ -131,7 +131,7 @@ open_http_post(struct http_post *http_post, const unsigned char *post_data,
if (new_files == NULL) { if (new_files == NULL) {
mem_free(filename); mem_free(filename);
done_http_post(http_post); done_http_post(http_post);
*error = S_OUT_OF_MEM; *error = connection_state(S_OUT_OF_MEM);
return 0; return 0;
} }
http_post->files = new_files; http_post->files = new_files;
@ -157,14 +157,14 @@ open_http_post(struct http_post *http_post, const unsigned char *post_data,
static int static int
read_http_post_inline(struct http_post *http_post, read_http_post_inline(struct http_post *http_post,
unsigned char buffer[], int max, unsigned char buffer[], int max,
enum connection_state *error) struct connection_state *error)
{ {
const unsigned char *post = http_post->post_data; const unsigned char *post = http_post->post_data;
const unsigned char *end = strchr(post, FILE_CHAR); const unsigned char *end = strchr(post, FILE_CHAR);
int total = 0; int total = 0;
assert(http_post->post_fd < 0); assert(http_post->post_fd < 0);
if_assert_failed { *error = S_INTERNAL; return -1; } if_assert_failed { *error = connection_state(S_INTERNAL); return -1; }
if (!end) if (!end)
end = strchr(post, '\0'); end = strchr(post, '\0');
@ -199,7 +199,7 @@ read_http_post_inline(struct http_post *http_post,
if (total > 0) if (total > 0)
return total; /* retry the open on the next call */ return total; /* retry the open on the next call */
else { else {
*error = -errno; *error = connection_state_for_errno(errno);
return -1; return -1;
} }
} }
@ -215,7 +215,7 @@ read_http_post_inline(struct http_post *http_post,
static int static int
read_http_post_fd(struct http_post *http_post, read_http_post_fd(struct http_post *http_post,
unsigned char buffer[], int max, unsigned char buffer[], int max,
enum connection_state *error) struct connection_state *error)
{ {
const struct http_post_file *const file const struct http_post_file *const file
= &http_post->files[http_post->file_index]; = &http_post->files[http_post->file_index];
@ -224,7 +224,7 @@ read_http_post_fd(struct http_post *http_post,
/* safe_read() would set errno = EBADF anyway, but check this /* safe_read() would set errno = EBADF anyway, but check this
* explicitly to make any such bugs easier to detect. */ * explicitly to make any such bugs easier to detect. */
assert(http_post->post_fd >= 0); assert(http_post->post_fd >= 0);
if_assert_failed { *error = S_INTERNAL; return -1; } if_assert_failed { *error = connection_state(S_INTERNAL); return -1; }
ret = safe_read(http_post->post_fd, buffer, max); ret = safe_read(http_post->post_fd, buffer, max);
if (ret <= 0) { if (ret <= 0) {
@ -237,7 +237,7 @@ read_http_post_fd(struct http_post *http_post,
* It will be cleared when the next file is opened. */ * It will be cleared when the next file is opened. */
if (ret == -1) { if (ret == -1) {
*error = -errno_from_read; *error = connection_state_for_errno(errno_from_read);
return -1; return -1;
} else if (http_post->file_read != file->size) { } else if (http_post->file_read != file->size) {
/* ELinks already sent a Content-Length header /* ELinks already sent a Content-Length header
@ -247,7 +247,7 @@ read_http_post_fd(struct http_post *http_post,
* enough data to fill the Content-Length. * enough data to fill the Content-Length.
* (Well, it could pad with zeroes, but that * (Well, it could pad with zeroes, but that
* would be just weird.) */ * would be just weird.) */
*error = S_HTTP_UPLOAD_RESIZED; *error = connection_state(S_HTTP_UPLOAD_RESIZED);
return -1; return -1;
} else { } else {
/* The upload file ended but there may still /* The upload file ended but there may still
@ -265,7 +265,7 @@ read_http_post_fd(struct http_post *http_post,
* been extended. Abort the connection because ELinks * been extended. Abort the connection because ELinks
* can no longer fit the entire file in the original * can no longer fit the entire file in the original
* Content-Length. */ * Content-Length. */
*error = S_HTTP_UPLOAD_RESIZED; *error = connection_state(S_HTTP_UPLOAD_RESIZED);
return -1; return -1;
} }
@ -282,7 +282,7 @@ read_http_post_fd(struct http_post *http_post,
int int
read_http_post(struct http_post *http_post, read_http_post(struct http_post *http_post,
unsigned char buffer[], int max, unsigned char buffer[], int max,
enum connection_state *error) struct connection_state *error)
{ {
int total = 0; int total = 0;

View File

@ -55,9 +55,9 @@ struct http_post {
void init_http_post(struct http_post *http_post); void init_http_post(struct http_post *http_post);
void done_http_post(struct http_post *http_post); void done_http_post(struct http_post *http_post);
int open_http_post(struct http_post *http_post, const unsigned char *post_data, int open_http_post(struct http_post *http_post, const unsigned char *post_data,
enum connection_state *error); struct connection_state *error);
int read_http_post(struct http_post *http_post, int read_http_post(struct http_post *http_post,
unsigned char buffer[], int max, unsigned char buffer[], int max,
enum connection_state *error); struct connection_state *error);
#endif #endif

View File

@ -106,13 +106,13 @@ init_nntp_connection_info(struct connection *conn)
assert(conn->info == NULL); assert(conn->info == NULL);
assert(conn->done == NULL); assert(conn->done == NULL);
if_assert_failed { if_assert_failed {
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
return NULL; return NULL;
} }
nntp = mem_calloc(1, sizeof(*nntp)); nntp = mem_calloc(1, sizeof(*nntp));
if (!nntp) { if (!nntp) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return NULL; return NULL;
} }
@ -150,7 +150,7 @@ init_nntp_connection_info(struct connection *conn)
break; break;
/* FIXME: Special S_NNTP_BAD_RANGE */ /* FIXME: Special S_NNTP_BAD_RANGE */
abort_connection(conn, S_BAD_URL); abort_connection(conn, connection_state(S_BAD_URL));
return NULL; return NULL;
case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_ARTICLE_NUMBER:
@ -164,12 +164,12 @@ init_nntp_connection_info(struct connection *conn)
/* Map nntp://<server>/<group> to nntp://<server>/<group>/ so /* Map nntp://<server>/<group> to nntp://<server>/<group>/ so
* we get only one cache entry with content. */ * we get only one cache entry with content. */
if (!groupend) { if (!groupend) {
enum connection_state state = S_OK; struct connection_state state = connection_state(S_OK);
conn->cached = get_cache_entry(conn->uri); conn->cached = get_cache_entry(conn->uri);
if (!conn->cached if (!conn->cached
|| !redirect_cache(conn->cached, "/", 0, 0)) || !redirect_cache(conn->cached, "/", 0, 0))
state = S_OUT_OF_MEM; state = connection_state(S_OUT_OF_MEM);
abort_connection(conn, state); abort_connection(conn, state);
return NULL; return NULL;
@ -177,7 +177,7 @@ init_nntp_connection_info(struct connection *conn)
/* Reject nntp://<server>/<group>/<group> */ /* Reject nntp://<server>/<group>/<group> */
if (nntp->group.source) { if (nntp->group.source) {
abort_connection(conn, S_BAD_URL); abort_connection(conn, connection_state(S_BAD_URL));
return NULL; return NULL;
} }
@ -204,13 +204,13 @@ nntp_quit(struct connection *conn)
assert(conn->info == NULL); assert(conn->info == NULL);
assert(conn->done == NULL); assert(conn->done == NULL);
if_assert_failed { if_assert_failed {
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
return; return;
} }
info = mem_calloc(1, sizeof(*info)); info = mem_calloc(1, sizeof(*info));
if (!info) { if (!info) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -222,7 +222,7 @@ nntp_quit(struct connection *conn)
} }
static void static void
nntp_end_request(struct connection *conn, enum connection_state state) nntp_end_request(struct connection *conn, struct connection_state state)
{ {
struct nntp_connection_info *nntp = conn->info; struct nntp_connection_info *nntp = conn->info;
@ -231,11 +231,11 @@ nntp_end_request(struct connection *conn, enum connection_state state)
return; return;
} }
if (state == S_OK) { if (is_in_state(state, S_OK)) {
if (conn->cached) { if (conn->cached) {
normalize_cache_entry(conn->cached, conn->from); normalize_cache_entry(conn->cached, conn->from);
} }
} else if (state == S_OUT_OF_MEM) { } else if (is_in_state(state, S_OUT_OF_MEM)) {
/* FIXME: Clear the socket buffers before ending so the one /* FIXME: Clear the socket buffers before ending so the one
* grabing the keepalive connection will be able to go on. */ * grabing the keepalive connection will be able to go on. */
} }
@ -253,38 +253,39 @@ read_nntp_data(struct socket *socket, struct read_buffer *rb)
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
if (socket->state == SOCKET_CLOSED) { if (socket->state == SOCKET_CLOSED) {
nntp_end_request(conn, S_OK); nntp_end_request(conn, connection_state(S_OK));
return; return;
} }
switch (read_nntp_response_data(conn, rb)) { switch (read_nntp_response_data(conn, rb).basic) {
case S_OK: case S_OK:
nntp_send_command(conn); nntp_send_command(conn);
break; break;
case S_OUT_OF_MEM: case S_OUT_OF_MEM:
nntp_end_request(conn, S_OUT_OF_MEM); nntp_end_request(conn, connection_state(S_OUT_OF_MEM));
break; break;
case S_TRANS: case S_TRANS:
default: default:
read_from_socket(conn->socket, rb, S_TRANS, read_nntp_data); read_from_socket(conn->socket, rb, connection_state(S_TRANS),
read_nntp_data);
} }
} }
/* Translate NNTP code to the internal connection state. */ /* Translate NNTP code to the internal connection state. */
static enum connection_state static struct connection_state
get_nntp_connection_state(enum nntp_code code) get_nntp_connection_state(enum nntp_code code)
{ {
switch (code) { switch (code) {
case NNTP_CODE_400_GOODBYE: return S_NNTP_SERVER_HANG_UP; case NNTP_CODE_400_GOODBYE: return connection_state(S_NNTP_SERVER_HANG_UP);
case NNTP_CODE_411_GROUP_UNKNOWN: return S_NNTP_GROUP_UNKNOWN; case NNTP_CODE_411_GROUP_UNKNOWN: return connection_state(S_NNTP_GROUP_UNKNOWN);
case NNTP_CODE_423_ARTICLE_NONUMBER: return S_NNTP_ARTICLE_UNKNOWN; case NNTP_CODE_423_ARTICLE_NONUMBER: return connection_state(S_NNTP_ARTICLE_UNKNOWN);
case NNTP_CODE_430_ARTICLE_NOID: return S_NNTP_ARTICLE_UNKNOWN; case NNTP_CODE_430_ARTICLE_NOID: return connection_state(S_NNTP_ARTICLE_UNKNOWN);
case NNTP_CODE_436_ARTICLE_TRANSFER: return S_NNTP_TRANSFER_ERROR; case NNTP_CODE_436_ARTICLE_TRANSFER: return connection_state(S_NNTP_TRANSFER_ERROR);
case NNTP_CODE_480_AUTH_REQUIRED: return S_NNTP_AUTH_REQUIRED; case NNTP_CODE_480_AUTH_REQUIRED: return connection_state(S_NNTP_AUTH_REQUIRED);
case NNTP_CODE_502_ACCESS_DENIED: return S_NNTP_ACCESS_DENIED; case NNTP_CODE_502_ACCESS_DENIED: return connection_state(S_NNTP_ACCESS_DENIED);
case NNTP_CODE_503_PROGRAM_FAULT: return S_NNTP_SERVER_ERROR; case NNTP_CODE_503_PROGRAM_FAULT: return connection_state(S_NNTP_SERVER_ERROR);
case NNTP_CODE_412_GROUP_UNSET: case NNTP_CODE_412_GROUP_UNSET:
case NNTP_CODE_420_ARTICLE_UNSET: case NNTP_CODE_420_ARTICLE_UNSET:
@ -301,7 +302,7 @@ get_nntp_connection_state(enum nntp_code code)
default: default:
/* Notice and error codes for stuff which is either not /* Notice and error codes for stuff which is either not
* supported or which is not supposed to happen. */ * supported or which is not supposed to happen. */
return S_NNTP_ERROR; return connection_state(S_NNTP_ERROR);
}; };
} }
@ -312,7 +313,7 @@ nntp_got_response(struct socket *socket, struct read_buffer *rb)
struct nntp_connection_info *nntp = conn->info; struct nntp_connection_info *nntp = conn->info;
if (socket->state == SOCKET_CLOSED) { if (socket->state == SOCKET_CLOSED) {
nntp_end_request(conn, S_OK); nntp_end_request(conn, connection_state(S_OK));
return; return;
} }
@ -320,11 +321,12 @@ nntp_got_response(struct socket *socket, struct read_buffer *rb)
switch (nntp->code) { switch (nntp->code) {
case NNTP_CODE_NONE: case NNTP_CODE_NONE:
read_from_socket(conn->socket, rb, S_TRANS, nntp_got_response); read_from_socket(conn->socket, rb, connection_state(S_TRANS),
nntp_got_response);
break; break;
case NNTP_CODE_INVALID: case NNTP_CODE_INVALID:
nntp_end_request(conn, S_NNTP_ERROR); nntp_end_request(conn, connection_state(S_NNTP_ERROR));
break; break;
case NNTP_CODE_200_HELLO: case NNTP_CODE_200_HELLO:
@ -334,7 +336,7 @@ nntp_got_response(struct socket *socket, struct read_buffer *rb)
break; break;
case NNTP_CODE_205_GOODBYE: case NNTP_CODE_205_GOODBYE:
nntp_end_request(conn, S_OK); nntp_end_request(conn, connection_state(S_OK));
break; break;
case NNTP_CODE_215_FOLLOW_GROUPS: case NNTP_CODE_215_FOLLOW_GROUPS:
@ -523,19 +525,20 @@ nntp_send_command(struct connection *conn)
nntp->command = get_nntp_command(nntp); nntp->command = get_nntp_command(nntp);
if (nntp->command == NNTP_COMMAND_NONE) { if (nntp->command == NNTP_COMMAND_NONE) {
nntp_end_request(conn, S_OK); nntp_end_request(conn, connection_state(S_OK));
return; return;
} }
if (!init_string(&req)) { if (!init_string(&req)) {
nntp_end_request(conn, S_OUT_OF_MEM); nntp_end_request(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
/* FIXME: Check non empty and < NNTP_MAX_COMMAND_LENGTH */ /* FIXME: Check non empty and < NNTP_MAX_COMMAND_LENGTH */
add_nntp_command_to_string(&req, nntp); add_nntp_command_to_string(&req, nntp);
request_from_socket(conn->socket, req.source, req.length, S_SENT, request_from_socket(conn->socket, req.source, req.length,
connection_state(S_SENT),
SOCKET_END_ONCLOSE, nntp_got_response); SOCKET_END_ONCLOSE, nntp_got_response);
done_string(&req); done_string(&req);
} }
@ -581,13 +584,13 @@ news_protocol_handler(struct connection *conn)
if (!*server) server = getenv("NNTPSERVER"); if (!*server) server = getenv("NNTPSERVER");
if (!server || !*server) { if (!server || !*server) {
abort_connection(conn, S_NNTP_NEWS_SERVER); abort_connection(conn, connection_state(S_NNTP_NEWS_SERVER));
return; return;
} }
conn->cached = get_cache_entry(conn->uri); conn->cached = get_cache_entry(conn->uri);
if (!conn->cached || !init_string(&location)) { if (!conn->cached || !init_string(&location)) {
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
@ -600,5 +603,5 @@ news_protocol_handler(struct connection *conn)
done_string(&location); done_string(&location);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }

View File

@ -89,14 +89,14 @@ get_nntp_message_header_end(unsigned char *data, int datalen)
return NULL; return NULL;
} }
static enum connection_state static struct connection_state
init_nntp_header(struct connection *conn, struct read_buffer *rb) init_nntp_header(struct connection *conn, struct read_buffer *rb)
{ {
struct nntp_connection_info *nntp = conn->info; struct nntp_connection_info *nntp = conn->info;
if (!conn->cached) { if (!conn->cached) {
conn->cached = get_cache_entry(conn->uri); conn->cached = get_cache_entry(conn->uri);
if (!conn->cached) return S_OUT_OF_MEM; if (!conn->cached) return connection_state(S_OUT_OF_MEM);
} else if (conn->cached->head || conn->cached->content_type) { } else if (conn->cached->head || conn->cached->content_type) {
/* If the head is set wipe out the content to be sure */ /* If the head is set wipe out the content to be sure */
@ -107,7 +107,7 @@ init_nntp_header(struct connection *conn, struct read_buffer *rb)
/* XXX: Override any Content-Type line in the header */ /* XXX: Override any Content-Type line in the header */
mem_free_set(&conn->cached->content_type, stracpy("text/html")); mem_free_set(&conn->cached->content_type, stracpy("text/html"));
if (!conn->cached->content_type) if (!conn->cached->content_type)
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
switch (nntp->target) { switch (nntp->target) {
case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_ARTICLE_NUMBER:
@ -119,12 +119,12 @@ init_nntp_header(struct connection *conn, struct read_buffer *rb)
end = get_nntp_message_header_end(rb->data, rb->length); end = get_nntp_message_header_end(rb->data, rb->length);
if (!end) { if (!end) {
/* Redo the whole cache entry thing next time */ /* Redo the whole cache entry thing next time */
return S_TRANS; return connection_state(S_TRANS);
} }
/* FIXME: Add the NNTP response code line */ /* FIXME: Add the NNTP response code line */
conn->cached->head = stracpy("FIXME NNTP response code\r\n"); conn->cached->head = stracpy("FIXME NNTP response code\r\n");
if (!conn->cached->head) return S_OUT_OF_MEM; if (!conn->cached->head) return connection_state(S_OUT_OF_MEM);
add_to_strn(&conn->cached->head, rb->data); add_to_strn(&conn->cached->head, rb->data);
@ -140,7 +140,7 @@ init_nntp_header(struct connection *conn, struct read_buffer *rb)
break; break;
} }
return S_OK; return connection_state(S_OK);
} }
@ -462,31 +462,31 @@ add_nntp_html_line(struct string *html, struct connection *conn,
add_char_to_string(html, '\n'); add_char_to_string(html, '\n');
} }
enum connection_state struct connection_state
read_nntp_response_data(struct connection *conn, struct read_buffer *rb) read_nntp_response_data(struct connection *conn, struct read_buffer *rb)
{ {
struct string html; struct string html;
unsigned char *end; unsigned char *end;
enum connection_state state = S_TRANS; struct connection_state state = connection_state(S_TRANS);
if (conn->from == 0) { if (conn->from == 0) {
switch (init_nntp_header(conn, rb)) { switch (init_nntp_header(conn, rb).basic) {
case S_OK: case S_OK:
break; break;
case S_OUT_OF_MEM: case S_OUT_OF_MEM:
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
case S_TRANS: case S_TRANS:
return S_TRANS; return connection_state(S_TRANS);
default: default:
return S_NNTP_ERROR; return connection_state(S_NNTP_ERROR);
} }
} }
if (!init_string(&html)) if (!init_string(&html))
return S_OUT_OF_MEM; return connection_state(S_OUT_OF_MEM);
if (conn->from == 0) if (conn->from == 0)
add_nntp_html_start(&html, conn); add_nntp_html_start(&html, conn);
@ -495,7 +495,7 @@ read_nntp_response_data(struct connection *conn, struct read_buffer *rb)
unsigned char *line = check_nntp_line(rb->data, end); unsigned char *line = check_nntp_line(rb->data, end);
if (!line) { if (!line) {
state = S_OK; state = connection_state(S_OK);
break; break;
} }
@ -505,7 +505,7 @@ read_nntp_response_data(struct connection *conn, struct read_buffer *rb)
kill_buffer_data(rb, end - rb->data); kill_buffer_data(rb, end - rb->data);
} }
if (state != S_TRANS) if (!is_in_state(state, S_TRANS))
add_nntp_html_end(&html, conn); add_nntp_html_end(&html, conn);
add_fragment(conn->cached, conn->from, html.source, html.length); add_fragment(conn->cached, conn->from, html.source, html.length);

View File

@ -13,7 +13,7 @@ struct read_buffer;
* S_TRANS (transferring) means 'end-of-text' not reached yet * S_TRANS (transferring) means 'end-of-text' not reached yet
* S_OK means no more text expected * S_OK means no more text expected
* S_OUT_OF_MEM allocation failure of some sort */ * S_OUT_OF_MEM allocation failure of some sort */
enum connection_state struct connection_state
read_nntp_response_data(struct connection *conn, struct read_buffer *rb); read_nntp_response_data(struct connection *conn, struct read_buffer *rb);
/* Reads the first line in the NNTP response from the @rb read buffer and /* Reads the first line in the NNTP response from the @rb read buffer and

View File

@ -212,7 +212,7 @@ static void
generic_external_protocol_handler(struct session *ses, struct uri *uri) generic_external_protocol_handler(struct session *ses, struct uri *uri)
{ {
/* [gettext_accelerator_context(generic_external_protocol_handler)] */ /* [gettext_accelerator_context(generic_external_protocol_handler)] */
enum connection_state state; struct connection_state state;
switch (uri->protocol) { switch (uri->protocol) {
case PROTOCOL_JAVASCRIPT: case PROTOCOL_JAVASCRIPT:
@ -220,18 +220,18 @@ generic_external_protocol_handler(struct session *ses, struct uri *uri)
ecmascript_protocol_handler(ses, uri); ecmascript_protocol_handler(ses, uri);
return; return;
#else #else
state = S_NO_JAVASCRIPT; state = connection_state(S_NO_JAVASCRIPT);
#endif #endif
break; break;
case PROTOCOL_UNKNOWN: case PROTOCOL_UNKNOWN:
state = S_UNKNOWN_PROTOCOL; state = connection_state(S_UNKNOWN_PROTOCOL);
break; break;
default: default:
#ifndef CONFIG_SSL #ifndef CONFIG_SSL
if (get_protocol_need_ssl(uri->protocol)) { if (get_protocol_need_ssl(uri->protocol)) {
state = S_SSL_ERROR; state = connection_state(S_SSL_ERROR);
break; break;
} }
#endif #endif

View File

@ -55,7 +55,7 @@ proxy_probe_no_proxy(unsigned char *url, unsigned char *no_proxy)
static struct uri * static struct uri *
proxy_uri(struct uri *uri, unsigned char *proxy, proxy_uri(struct uri *uri, unsigned char *proxy,
enum connection_state *connection_state) struct connection_state *error_state)
{ {
struct string string; struct string string;
@ -70,10 +70,10 @@ proxy_uri(struct uri *uri, unsigned char *proxy,
/* XXX: Assume the problem is due to @proxy having bad format. /* XXX: Assume the problem is due to @proxy having bad format.
* This is a lot faster easier than checking the format. */ * This is a lot faster easier than checking the format. */
if (!uri) if (!uri)
*connection_state = S_PROXY_ERROR; *error_state = connection_state(S_PROXY_ERROR);
} else { } else {
uri = NULL; uri = NULL;
*connection_state = S_OUT_OF_MEM; *error_state = connection_state(S_OUT_OF_MEM);
} }
done_string(&string); done_string(&string);
@ -118,7 +118,7 @@ get_protocol_proxy(unsigned char *opt,
static struct uri * static struct uri *
get_proxy_worker(struct uri *uri, unsigned char *proxy, get_proxy_worker(struct uri *uri, unsigned char *proxy,
enum connection_state *connection_state) struct connection_state *error_state)
{ {
unsigned char *protocol_proxy = NULL; unsigned char *protocol_proxy = NULL;
@ -126,7 +126,7 @@ get_proxy_worker(struct uri *uri, unsigned char *proxy,
if (*proxy) { if (*proxy) {
proxy = strip_proxy_protocol(proxy, "http://", "ftp://"); proxy = strip_proxy_protocol(proxy, "http://", "ftp://");
return proxy_uri(uri, proxy, connection_state); return proxy_uri(uri, proxy, error_state);
} }
/* "" from script_hook_get_proxy() */ /* "" from script_hook_get_proxy() */
@ -177,14 +177,14 @@ get_proxy_worker(struct uri *uri, unsigned char *proxy,
if (!no_proxy || !*no_proxy) no_proxy = getenv("no_proxy"); if (!no_proxy || !*no_proxy) no_proxy = getenv("no_proxy");
if (!proxy_probe_no_proxy(uri->host, no_proxy)) if (!proxy_probe_no_proxy(uri->host, no_proxy))
return proxy_uri(uri, protocol_proxy, connection_state); return proxy_uri(uri, protocol_proxy, error_state);
} }
return get_composed_uri(uri, URI_BASE); return get_composed_uri(uri, URI_BASE);
} }
struct uri * struct uri *
get_proxy_uri(struct uri *uri, enum connection_state *connection_state) get_proxy_uri(struct uri *uri, struct connection_state *error_state)
{ {
if (uri->protocol == PROTOCOL_PROXY) { if (uri->protocol == PROTOCOL_PROXY) {
return get_composed_uri(uri, URI_BASE); return get_composed_uri(uri, URI_BASE);
@ -196,11 +196,11 @@ get_proxy_uri(struct uri *uri, enum connection_state *connection_state)
set_event_id(get_proxy_event_id, "get-proxy"); set_event_id(get_proxy_event_id, "get-proxy");
trigger_event(get_proxy_event_id, &tmp, struri(uri)); trigger_event(get_proxy_event_id, &tmp, struri(uri));
uri = get_proxy_worker(uri, tmp, connection_state); uri = get_proxy_worker(uri, tmp, error_state);
mem_free_if(tmp); mem_free_if(tmp);
return uri; return uri;
#else #else
return get_proxy_worker(uri, NULL, connection_state); return get_proxy_worker(uri, NULL, error_state);
#endif #endif
} }
} }

View File

@ -1,14 +1,14 @@
#ifndef EL__PROTOCOL_PROXY_H #ifndef EL__PROTOCOL_PROXY_H
#define EL__PROTOCOL_PROXY_H #define EL__PROTOCOL_PROXY_H
enum connection_state; struct connection_state;
struct uri; struct uri;
/* Checks if the passed URI has been configured to go through a proxy. The /* Checks if the passed URI has been configured to go through a proxy. The
* fragment is removed from the returned URI. */ * fragment is removed from the returned URI. */
/* If @connection_state is non-NULL it will be set to indicate what error /* If @connection_state is non-NULL it will be set to indicate what error
* occurred if the function returns NULL. */ * occurred if the function returns NULL. */
struct uri *get_proxy_uri(struct uri *uri, enum connection_state *connection_state); struct uri *get_proxy_uri(struct uri *uri, struct connection_state *connection_state);
/* ``Translates'' the passed URI into the URI being proxied. If it is not a /* ``Translates'' the passed URI into the URI being proxied. If it is not a
* proxy:// URI it will return the URI with the fragment removed. */ * proxy:// URI it will return the URI with the fragment removed. */

View File

@ -71,10 +71,13 @@ static FILE *header_out, *data_out;
* process, and it would be very cumbersome to free those. */ * process, and it would be very cumbersome to free those. */
static void static void
smb_error(int error) smb_error(struct connection_state error)
{ {
if (is_system_error(error))
fprintf(data_out, "S%d\n", (int) error.syserr);
else
fprintf(data_out, "I%d\n", (int) error.basic);
fputs("text/x-error", header_out); fputs("text/x-error", header_out);
fprintf(data_out, "%d\n", error);
exit(1); exit(1);
} }
@ -252,8 +255,8 @@ smb_directory(int dir, struct uri *uri)
struct string buf; struct string buf;
unsigned char dircolor[8] = ""; unsigned char dircolor[8] = "";
if (init_directory_listing(&buf, uri) != S_OK) { if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) {
smb_error(-S_OUT_OF_MEM); smb_error(connection_state(S_OUT_OF_MEM));
} }
fputs("text/html", header_out); fputs("text/html", header_out);
@ -297,7 +300,7 @@ do_smb(struct connection *conn)
unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA); unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA);
if (!uri_string || !init_string(&string)) { if (!uri_string || !init_string(&string)) {
smb_error(-S_OUT_OF_MEM); smb_error(connection_state(S_OUT_OF_MEM));
} }
/* Must URI-encode the username and password to avoid /* Must URI-encode the username and password to avoid
* ambiguity if they contain "/:@" characters. * ambiguity if they contain "/:@" characters.
@ -320,10 +323,10 @@ do_smb(struct connection *conn)
} }
if (!url) { if (!url) {
smb_error(-S_OUT_OF_MEM); smb_error(connection_state(S_OUT_OF_MEM));
} }
if (smbc_init(smb_auth, 0)) { if (smbc_init(smb_auth, 0)) {
smb_error(errno); smb_error(connection_state_for_errno(errno));
}; };
dir = smbc_opendir(url); dir = smbc_opendir(url);
@ -345,12 +348,12 @@ do_smb(struct connection *conn)
* for credentials. */ * for credentials. */
if (errno == ENOENT && errno_from_opendir == EACCES) if (errno == ENOENT && errno_from_opendir == EACCES)
errno = errno_from_opendir; errno = errno_from_opendir;
smb_error(errno); smb_error(connection_state_for_errno(errno));
} }
res = smbc_fstat(file, &sb); res = smbc_fstat(file, &sb);
if (res) { if (res) {
smb_error(res); smb_error(connection_state_for_errno(res));
} }
/* filesize */ /* filesize */
fprintf(header_out, "%" OFF_PRINT_FORMAT, fprintf(header_out, "%" OFF_PRINT_FORMAT,
@ -375,7 +378,7 @@ static void
prompt_username_pw(struct connection *conn) prompt_username_pw(struct connection *conn)
{ {
add_auth_entry(conn->uri, "Samba", NULL, NULL, 0); add_auth_entry(conn->uri, "Samba", NULL, NULL, 0);
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
} }
static void static void
@ -383,10 +386,10 @@ smb_got_error(struct socket *socket, struct read_buffer *rb)
{ {
int len = rb->length; int len = rb->length;
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
int error; struct connection_state error;
if (len < 0) { if (len < 0) {
abort_connection(conn, -errno); abort_connection(conn, connection_state_for_errno(errno));
return; return;
} }
@ -396,20 +399,28 @@ smb_got_error(struct socket *socket, struct read_buffer *rb)
* pipe. */ * pipe. */
assert(rb->freespace >= 1); assert(rb->freespace >= 1);
if_assert_failed { if_assert_failed {
abort_connection(conn, S_INTERNAL); abort_connection(conn, connection_state(S_INTERNAL));
return; return;
} }
rb->data[len] = '\0'; rb->data[len] = '\0';
error = atoi(rb->data); switch (rb->data[0]) {
kill_buffer_data(rb, len); case 'S':
switch (error) { error = connection_state_for_errno(atoi(rb->data + 1));
case EACCES: break;
prompt_username_pw(conn); case 'I':
error = connection_state(atoi(rb->data + 1));
break; break;
default: default:
abort_connection(conn, -error); ERROR("malformed error code: %s", rb->data);
error = connection_state(S_INTERNAL);
break; break;
} }
kill_buffer_data(rb, len);
if (is_system_error(error) && error.syserr == EACCES)
prompt_username_pw(conn);
else
abort_connection(conn, error);
} }
static void static void
@ -419,12 +430,12 @@ smb_got_data(struct socket *socket, struct read_buffer *rb)
struct connection *conn = socket->conn; struct connection *conn = socket->conn;
if (len < 0) { if (len < 0) {
abort_connection(conn, -errno); abort_connection(conn, connection_state_for_errno(errno));
return; return;
} }
if (!len) { if (!len) {
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
@ -435,7 +446,7 @@ smb_got_data(struct socket *socket, struct read_buffer *rb)
conn->from += len; conn->from += len;
kill_buffer_data(rb, len); kill_buffer_data(rb, len);
read_from_socket(socket, rb, S_TRANS, smb_got_data); read_from_socket(socket, rb, connection_state(S_TRANS), smb_got_data);
} }
static void static void
@ -454,7 +465,7 @@ smb_got_header(struct socket *socket, struct read_buffer *rb)
* and assume abort_connection will do them?) */ * and assume abort_connection will do them?) */
close_socket(socket); close_socket(socket);
close_socket(conn->data_socket); close_socket(conn->data_socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
socket->state = SOCKET_END_ONCLOSE; socket->state = SOCKET_END_ONCLOSE;
@ -477,7 +488,7 @@ smb_got_header(struct socket *socket, struct read_buffer *rb)
/* avoid error */ /* avoid error */
if (!conn->est_length) { if (!conn->est_length) {
abort_connection(conn, S_OK); abort_connection(conn, connection_state(S_OK));
return; return;
} }
} }
@ -492,14 +503,16 @@ smb_got_header(struct socket *socket, struct read_buffer *rb)
if (!buf) { if (!buf) {
close_socket(socket); close_socket(socket);
close_socket(conn->data_socket); close_socket(conn->data_socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
if (error) { if (error) {
mem_free_set(&conn->cached->content_type, stracpy("text/html")); mem_free_set(&conn->cached->content_type, stracpy("text/html"));
read_from_socket(conn->data_socket, buf, S_CONN, smb_got_error); read_from_socket(conn->data_socket, buf,
connection_state(S_CONN), smb_got_error);
} else { } else {
read_from_socket(conn->data_socket, buf, S_CONN, smb_got_data); read_from_socket(conn->data_socket, buf,
connection_state(S_CONN), smb_got_data);
} }
} }
@ -535,7 +548,7 @@ smb_protocol_handler(struct connection *conn)
if (smb_pipe[1] >= 0) close(smb_pipe[1]); if (smb_pipe[1] >= 0) close(smb_pipe[1]);
if (header_pipe[0] >= 0) close(header_pipe[0]); if (header_pipe[0] >= 0) close(header_pipe[0]);
if (header_pipe[1] >= 0) close(header_pipe[1]); if (header_pipe[1] >= 0) close(header_pipe[1]);
abort_connection(conn, -s_errno); abort_connection(conn, connection_state_for_errno(s_errno));
return; return;
} }
conn->from = 0; conn->from = 0;
@ -550,7 +563,7 @@ smb_protocol_handler(struct connection *conn)
close(smb_pipe[1]); close(smb_pipe[1]);
close(header_pipe[0]); close(header_pipe[0]);
close(header_pipe[1]); close(header_pipe[1]);
retry_connection(conn, -s_errno); retry_connection(conn, connection_state_for_errno(s_errno));
return; return;
} }
@ -594,9 +607,10 @@ smb_protocol_handler(struct connection *conn)
if (!buf2) { if (!buf2) {
close_socket(conn->data_socket); close_socket(conn->data_socket);
close_socket(conn->socket); close_socket(conn->socket);
abort_connection(conn, S_OUT_OF_MEM); abort_connection(conn, connection_state(S_OUT_OF_MEM));
return; return;
} }
read_from_socket(conn->socket, buf2, S_CONN, smb_got_header); read_from_socket(conn->socket, buf2,
connection_state(S_CONN), smb_got_header);
} }
} }

View File

@ -85,7 +85,7 @@ get_user_program(struct terminal *term, unsigned char *progid, int progidlen)
/* declared in "session/session.h" */ /* declared in "session/session.h" */
void void
print_error_dialog(struct session *ses, enum connection_state state, print_error_dialog(struct session *ses, struct connection_state state,
struct uri *uri, enum connection_priority priority) struct uri *uri, enum connection_priority priority)
{ {
stub_called("print_error_dialog"); stub_called("print_error_dialog");

View File

@ -68,7 +68,7 @@ int
download_is_progressing(struct download *download) download_is_progressing(struct download *download)
{ {
return download return download
&& download->state == S_TRANS && is_in_state(download->state, S_TRANS)
&& has_progress(download->progress); && has_progress(download->progress);
} }
@ -318,9 +318,9 @@ download_data_store(struct download *download, struct file_download *file_downlo
return; return;
} }
if (download->state != S_OK) { if (!is_in_state(download->state, S_OK)) {
unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC); unsigned char *url = get_uri_string(file_download->uri, URI_PUBLIC);
enum connection_state state = download->state; struct connection_state state = download->state;
abort_download_and_beep(file_download, term); abort_download_and_beep(file_download, term);
@ -398,7 +398,7 @@ download_data(struct download *download, struct file_download *file_download)
done_uri(file_download->uri); done_uri(file_download->uri);
file_download->uri = get_uri_reference(cached->redirect); file_download->uri = get_uri_reference(cached->redirect);
file_download->download.state = S_WAIT_REDIR; file_download->download.state = connection_state(S_WAIT_REDIR);
if (file_download->dlg_data) if (file_download->dlg_data)
redraw_dialog(file_download->dlg_data, 1); redraw_dialog(file_download->dlg_data, 1);
@ -962,7 +962,7 @@ init_type_query(struct session *ses, struct download *download,
object_lock(type_query->cached); object_lock(type_query->cached);
move_download(download, &type_query->download, PRI_MAIN); move_download(download, &type_query->download, PRI_MAIN);
download->state = S_OK; download->state = connection_state(S_OK);
add_to_list(ses->type_queries, type_query); add_to_list(ses->type_queries, type_query);

View File

@ -41,8 +41,8 @@ struct download {
void *data; void *data;
struct progress *progress; struct progress *progress;
enum connection_state state; struct connection_state state;
enum connection_state prev_error; struct connection_state prev_error;
enum connection_priority pri; enum connection_priority pri;
}; };

View File

@ -241,7 +241,7 @@ get_current_download(struct session *ses)
else if (have_location(ses)) else if (have_location(ses))
download = &cur_loc(ses)->download; download = &cur_loc(ses)->download;
if (download && download->state == S_OK) { if (download && is_in_state(download->state, S_OK)) {
struct file_to_load *ftl; struct file_to_load *ftl;
foreach (ftl, ses->more_files) foreach (ftl, ses->more_files)
@ -255,7 +255,7 @@ get_current_download(struct session *ses)
} }
void void
print_error_dialog(struct session *ses, enum connection_state state, print_error_dialog(struct session *ses, struct connection_state state,
struct uri *uri, enum connection_priority priority) struct uri *uri, enum connection_priority priority)
{ {
struct string msg; struct string msg;
@ -602,7 +602,7 @@ doc_loading_callback(struct download *download, struct session *ses)
start_document_refreshes(ses); start_document_refreshes(ses);
if (download->state != S_OK) { if (!is_in_state(download->state, S_OK)) {
print_error_dialog(ses, download->state, print_error_dialog(ses, download->state,
ses->doc_view->document->uri, ses->doc_view->document->uri,
download->pri); download->pri);
@ -838,7 +838,7 @@ setup_session(struct session *ses, struct uri *uri, struct session *base)
struct location *loc = mem_calloc(1, sizeof(*loc)); struct location *loc = mem_calloc(1, sizeof(*loc));
if (loc) { if (loc) {
loc->download.state = S_OK; loc->download.state = connection_state(S_OK);
copy_location(loc, cur_loc(base)); copy_location(loc, cur_loc(base));
add_to_history(&ses->history, loc); add_to_history(&ses->history, loc);
render_document_frames(ses, 0); render_document_frames(ses, 0);
@ -1120,7 +1120,8 @@ decode_session_info(struct terminal *term, struct terminal_info *info)
/* End loop if initialization fails */ /* End loop if initialization fails */
len = 0; len = 0;
} else if (bad_url) { } else if (bad_url) {
print_error_dialog(ses, S_BAD_URL, NULL, PRI_MAIN); print_error_dialog(ses, connection_state(S_BAD_URL),
NULL, PRI_MAIN);
} }
} }

View File

@ -231,7 +231,7 @@ have_location(struct session *ses) {
void set_session_referrer(struct session *ses, struct uri *referrer); void set_session_referrer(struct session *ses, struct uri *referrer);
void void
print_error_dialog(struct session *ses, enum connection_state state, print_error_dialog(struct session *ses, struct connection_state state,
struct uri *uri, enum connection_priority priority); struct uri *uri, enum connection_priority priority);
void process_file_requests(struct session *); void process_file_requests(struct session *);

View File

@ -543,7 +543,8 @@ loading_callback(struct download *download, struct session *ses)
if (d == DO_MOVE_DISPLAY) doc_loading_callback(download, ses); if (d == DO_MOVE_DISPLAY) doc_loading_callback(download, ses);
} }
if (is_in_result_state(download->state) && download->state != S_OK) { if (is_in_result_state(download->state)
&& !is_in_state(download->state, S_OK)) {
print_error_dialog(ses, download->state, print_error_dialog(ses, download->state,
download->conn ? download->conn->uri : NULL, download->conn ? download->conn->uri : NULL,
download->pri); download->pri);
@ -564,7 +565,7 @@ do_follow_url(struct session *ses, struct uri *uri, unsigned char *target,
protocol_external_handler_T *external_handler; protocol_external_handler_T *external_handler;
if (!uri) { if (!uri) {
print_error_dialog(ses, S_BAD_URL, uri, PRI_CANCEL); print_error_dialog(ses, connection_state(S_BAD_URL), uri, PRI_CANCEL);
return; return;
} }

View File

@ -2,7 +2,6 @@
#define EL__VIEWER_ACTION_H #define EL__VIEWER_ACTION_H
#include "config/kbdbind.h" #include "config/kbdbind.h"
#include "viewer/text/view.h"
struct session; struct session;

View File

@ -263,7 +263,7 @@ dump_loading_callback(struct download *download, void *p)
} }
if (download->state != S_OK) { if (!is_in_state(download->state, S_OK)) {
usrerror(get_state_message(download->state, NULL)); usrerror(get_state_message(download->state, NULL));
program.retval = RET_ERROR; program.retval = RET_ERROR;
goto terminate; goto terminate;

View File

@ -378,7 +378,13 @@ draw_formatted(struct session *ses, int rerender)
void void
refresh_view(struct session *ses, struct document_view *doc_view, int frames) refresh_view(struct session *ses, struct document_view *doc_view, int frames)
{ {
draw_doc(ses, doc_view, 1); /* If refresh_view() is being called because the value of a
if (frames) draw_frames(ses); * form field has changed, @ses might not be in the current
* tab: consider SELECT pop-ups behind which -remote loads
* another tab, or setTimeout in ECMAScript. */
if (ses->tab == get_current_tab(ses->tab->term)) {
draw_doc(ses, doc_view, 1);
if (frames) draw_frames(ses);
}
print_screen_status(ses); print_screen_status(ses);
} }