├── LICENSE
├── Makefile
├── README
├── minimal-conf.lua
├── port70.lua
├── port70
├── cgi.lua
├── getuserdir.c
├── handlers
│ ├── content.lua
│ ├── file.lua
│ ├── filesystem.lua
│ ├── http.lua
│ ├── sample.lua
│ ├── url.lua
│ └── userdir.lua
├── mklink.lua
├── readfile.lua
├── safetext.lua
└── setugid.c
├── sample-conf.lua
└── share
├── about.txt
├── index.gopher
├── index.port70
└── test
└── hello.txt
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #########################################################################
2 | #
3 | # Copyright 2019 by Sean Conner. All Rights Reserved.
4 | #
5 | # This library is free software; you can redistribute it and/or modify it
6 | # under the terms of the GNU Lesser General Public License as published by
7 | # the Free Software Foundation; either version 3 of the License, or (at your
8 | # option) any later version.
9 | #
10 | # This library is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 | # License for more details.
14 | #
15 | # You should have received a copy of the GNU Lesser General Public License
16 | # along with this library; if not, see .
17 | #
18 | # Comments, questions and criticisms can be sent to: sean@conman.org
19 | #
20 | ########################################################################
21 |
22 | CC = gcc -std=c99
23 | CFLAGS = -g -Wall -Wextra -pedantic
24 | LDFLAGS = -g -shared
25 |
26 | override CFLAGS += -fPIC -D_GNU_SOURCE
27 |
28 | INSTALL = /usr/bin/install
29 | INSTALL_PROGRAM = $(INSTALL)
30 | INSTALL_DATA = $(INSTALL) -m 644
31 |
32 | prefix = /usr/local
33 | datarootdir = $(prefix)/share
34 | exec_prefix = $(prefix)
35 | bindir = $(exec_prefix)/bin
36 | libdir = $(exec_prefix)/lib
37 |
38 | LUA ?= lua
39 | LUA_VERSION := $(shell $(LUA) -e "print(_VERSION:match '^Lua (.*)')")
40 | LUADIR ?= $(datarootdir)/lua/$(LUA_VERSION)
41 | LIBDIR ?= $(libdir)/lua/$(LUA_VERSION)
42 | BINDIR ?= $(exec_prefix)/bin
43 |
44 | ifneq ($(LUA_INCDIR),)
45 | override CFLAGS += -I$(LUA_INCDIR)
46 | endif
47 |
48 | # ===================================================
49 |
50 | %.so : %.c
51 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)
52 |
53 | # ===================================================
54 |
55 | .PHONY: all luacheck clean
56 |
57 | all: port70/getuserdir.so port70/setugid.so
58 |
59 | luacheck:
60 | luacheck $(shell find . -name '*.lua')
61 |
62 | clean:
63 | $(RM) $(shell find . -name '*~')
64 | $(RM) $(shell find . -name '*.so')
65 |
66 | install: all
67 | $(INSTALL) -d $(DESTDIR)$(BINDIR)
68 | $(INSTALL) -d $(DESTDIR)$(LIBDIR)/port70
69 | $(INSTALL) -d $(DESTDIR)$(LUADIR)/port70
70 | $(INSTALL) -d $(DESTDIR)$(LUADIR)/port70/handlers
71 | $(INSTALL_DATA) port70/*.lua $(DESTDIR)$(LUADIR)/port70
72 | $(INSTALL_DATA) port70/handlers/*.lua $(DESTDIR)$(LUADIR)/port70/handlers
73 | $(INSTALL_PROGRAM) port70/*.so $(DESTDIR)$(LIBDIR)/port70
74 | $(INSTALL_PROGRAM) port70.lua $(DESTDIR)$(BINDIR)/port70
75 |
76 | uninstall:
77 | $(RM) -r $(DESTDIR)$(LUADIR)/port70
78 | $(RM) -r $(DESTDIR)$(LIBDIR)/port70
79 | $(RM) -r $(DESTDIR)$(BINDIR)/port70
80 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 |
2 | port70 - a Gopher server (RFC-1436) written in Lua. This currently runs my
3 | own personal Gopher server at but should be
4 | usable by people other than myself, unlike my previous Gopher server.
5 |
6 | Features:
7 |
8 | * Configurable filenames for Gopher index files.
9 | * A much nicer Gopher index file format native to this program.
10 | * Generates Gopher indexes for directories that lack a Gopher index file.
11 | * Support for user directories.
12 | * Support for URL: selectors.
13 | * Support for CGI scripts
14 | * Easily extensible with support for custom handlers.
15 | * Experiemental support for selector redirection.
16 | * Configuration via file.
17 |
18 | Prerequisites:
19 |
20 | * Lua >= 5.3
21 | * GNU libmagic (NOTE: this is NOT image manipulation, but file
22 | identification)
23 | * LPEG >= 1.0.0
24 | * A whole bunch of other Lua modules, available at:
25 | * https://github.com/spc476/lua-conmanorg
26 | * https://github.com/spc476/LPeg-Parsers
27 |
28 | Installation:
29 |
30 | This will require some work. You will need to download the modules listed
31 | above from Github and get them installed. The lua-conmanorg repository will
32 | take some futzing around to get it compiled (first hint: remove any mention
33 | of tcc in the Makefile). But once you have all the prerequisites installed,
34 | the server should just work.
35 |
36 | Bulding:
37 |
38 | Just type "make". If you have the Lua include files in a non-standard
39 | location, you may need to do:
40 |
41 | make LUA_INCDIR=/path/to/Lua/includes
42 |
43 | or even
44 |
45 | make LUA= LUA_INCDIR=/path/to/Lua/includes
46 |
47 | You can do "make install" if you want to install the server onto the system:
48 |
49 | make install
50 |
51 | This will install files in the following locations:
52 |
53 | /usr/local/bin/port70 # server
54 | /usr/local/lib/lua/5.3/port70/ # server modules (written in C)
55 | /usr/local/share/lua/5.3/port70/ # server modules (written in Lua)
56 |
57 | To fine tune, you can from the command line:
58 |
59 | make [location] install
60 |
61 | The various [location] options are (multiple can be specified)
62 |
63 | prefix=target-dir
64 |
65 | target-dir/bin/port70
66 | target-dir/lib/lua/5.3/port70/
67 | target-dir/share/lua/5.3/port70/
68 |
69 | LUADIR=target-dir
70 |
71 | target-dir/port70/ # modules written in Lua
72 |
73 | LIBDIR=target-dir
74 |
75 | target-dir/port70/ # modules written in C
76 |
77 | BINDIR=target-dir
78 |
79 | target-dir/port70/ # server program
80 |
81 | Configuration:
82 |
83 | The file "minimal-conf.lua" is the bare minimum configuration file you'll
84 | need to serve up files from a directory, but do look at the sample-conf.lua
85 | file as that has extensive comments about each section and what is required,
86 | and what isn't. It will help to know some Lua, especially Lua string
87 | patterns (its form of regex) as various sections of the configuration are
88 | applied to requests via pattern matching of the request.
89 |
90 | Gopher Index files
91 |
92 | While the standard gopher index file format described in RFC-1436:
93 |
94 | 0This is a filefile-selectorexample.com70
95 |
96 | this program does have its own gopher index file that is easier to write and
97 | less error prone. Lines that do NOT start with a tab () are converted
98 | as is to the gopher 'i' type (information). Lines that do start with a tab
99 | are converted. The generalized format is:
100 |
101 | typeselectordescription
102 |
103 | The following types supported are:
104 |
105 | file selector refers to a text file (type 0)
106 | dir selector refers to a directory (type 1)
107 | CSO selector refers to a CSO phone-book server (type 2)
108 | binhex selector refers to a BinHexed Macintosh file (type 4)
109 | EXE selector refers to an executable file (type 5)
110 | uuencode selector refers to a UNIX uuencoded file (type 6)
111 | search selector refers to an Index-Search server (type 7)
112 | telnet selector refers to a text-based telnet session (type 8)
113 | binary selector refers to a binary file (type 9)
114 | gif selector refers to a GIF file (type g)
115 | image selector refers to a non-GIF image file (type I)
116 | html selector refers to an HTML file (type h)
117 | url selector is a URL (more below)
118 |
119 | The 'url' type is special since the selector is an actual URL---what type is
120 | used determined by the URL given (a full URL is expected, with scheme).
121 | Example:
122 |
123 | url gopher://gopher.example.com/ Some random server
124 |
125 | will create a type 1 entry (directory) with a blank selector, a host of
126 | "gopher.example.com" and a port number of 70.
127 |
128 | url https://www.example.com/ Some other random server
129 |
130 | will create a type h entry (HTML) with a selector of
131 | "URL:https://www.example.com".
132 |
133 | One final type is supported to allow dynamic content in the page to be
134 | generated. If the type is "Lua{", the following lines until a "Lua}" will
135 | be collected and executed as Lua code. The only global available will be
136 | the require() function, allowing one to include other Lua modules without
137 | polluting the namespace of the main program.
138 |
139 | The "share/index.port70" is an example of this format. It will generate a
140 | file that closely matches the "share/index.gopher" file (sans the dynamic
141 | output).
142 |
--------------------------------------------------------------------------------
/minimal-conf.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Mininal configuration file.
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals network handlers
23 |
24 | network =
25 | {
26 | host = "example.com",
27 | }
28 |
29 | handlers =
30 | {
31 | {
32 | selector = "",
33 | module = "port70.handlers.filesystem",
34 | directory = "share", -- change as needed
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/port70.lua:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env lua
2 | -- ***********************************************************************
3 | --
4 | -- Copyright 2019 by Sean Conner.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify it
7 | -- under the terms of the GNU General Public License as published by the
8 | -- Free Software Foundation, either version 3 of the License, or (at your
9 | -- option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful, but
12 | -- WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14 | -- Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License along
17 | -- with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ***********************************************************************
22 | -- luacheck: ignore 611
23 |
24 | local syslog = require "org.conman.syslog"
25 | local signal = require "org.conman.signal"
26 | local nfl = require "org.conman.nfl"
27 | local tcp = require "org.conman.nfl.tcp"
28 | local exit = require "org.conman.const.exit"
29 | local lpeg = require "lpeg"
30 | local setugid = require "port70.setugid"
31 |
32 | math.randomseed(require("org.conman.math").seed())
33 | require("org.conman.fsys.magic"):flags('mime')
34 |
35 | -- ************************************************************************
36 |
37 | if #arg == 0 then
38 | io.stderr:write(string.format("usage: %s configfile\n",arg[0]))
39 | os.exit(exit.USAGE,true)
40 | end
41 |
42 | local CONF = {} do
43 | local conf,err = loadfile(arg[1],"t",CONF)
44 | if not conf then
45 | io.stderr:write(string.format("%s: %s\n",arg[1],err))
46 | os.exit(exit.CONFIG,true)
47 | end
48 |
49 | conf()
50 |
51 | if not CONF.syslog then
52 | CONF.syslog = { ident = 'gopher' , facility = 'daemon' }
53 | else
54 | CONF.syslog.ident = CONF.syslog.ident or 'gopher'
55 | CONF.syslog.facility = CONF.syslog.facility or 'daemon'
56 | end
57 |
58 | syslog.open(CONF.syslog.ident,CONF.syslog.facility)
59 |
60 | if not CONF.network
61 | or not CONF.network.host then
62 | syslog('critical',"%s: missing or bad network configuration",arg[1])
63 | io.stderr:write(string.format("%s: missing or bad network configuration",arg[1]),"\n")
64 | os.exit(exit.CONFIG,true)
65 | end
66 |
67 | if not CONF.network.addr then
68 | CONF.network.addr = "::"
69 | end
70 |
71 | if not CONF.network.port then
72 | CONF.network.port = 70
73 | end
74 |
75 | if not CONF.redirect then
76 | CONF.redirect = { permanent = {} , gone = {} }
77 | else
78 | CONF.redirect.permanent = CONF.redirect.permanent or {}
79 | CONF.redirect.gone = CONF.redirect.gone or {}
80 | end
81 |
82 | package.loaded['port70.CONF'] = CONF
83 |
84 | if not CONF.handlers or #CONF.handlers == 0 then
85 | syslog('critical',"%s: at least one handler needs to be defined",arg[1])
86 | io.stderr:write(string.format("%s: at least one handler needs to be defined",arg[1]),"\n")
87 | os.exit(exit.CONFIG,true)
88 | end
89 |
90 | local function loadmodule(info)
91 | local function notfound()
92 | return false,"Selector not found"
93 | end
94 |
95 | if not info.selector then
96 | syslog('error',"%q: missing selector field",info.module or "")
97 | io.stderr:write(string.format("%q: missing selector field",info.module or ""),"\n")
98 | info.selector = ""
99 | info.code = { handler = notfound }
100 | return
101 | end
102 |
103 | if not info.module then
104 | syslog('error',"%q: missing module field",info.selector or "")
105 | io.stderr:write(string.format("%q: missing module field",info.selector or ""),"\n")
106 | info.code = { handler = notfound }
107 | return
108 | end
109 |
110 | local okay,mod = pcall(require,info.module)
111 | if not okay then
112 | syslog('error',"%q %s",info.selector,mod)
113 | io.stderr:write(string.format("%q %s",info.selector,mod),"\n")
114 | info.code = { handler = notfound }
115 | return
116 | end
117 |
118 | if type(mod) ~= 'table' then
119 | syslog('error',"%q module %s not supported",info.selector,info.module)
120 | io.stderr:write(string.format("%q module %s not supported",info.selector,info.module),"\n")
121 | info.code = { handler = notfound }
122 | return
123 | end
124 |
125 | if not mod.handler then
126 | syslog('error',"%q missing %s.handler()",info.selector,info.module)
127 | io.stderr:write(string.format("%q missing %s.handler()",info.selector,info.module),"\n")
128 | mod.handler = notfound
129 | return
130 | end
131 |
132 | if mod.init then
133 | okay,err = mod.init(info)
134 | if not okay then
135 | syslog('error',"%q %s=%s",info.selector,info.module,err)
136 | io.stderr:write(string.format("%q %s=%s",info.selector,info.module,err),"\n")
137 | mod.handler = notfound
138 | return
139 | end
140 | end
141 |
142 | info.code = mod
143 | end
144 |
145 | table.sort(CONF.handlers,function(a,b)
146 | return #a.selector == #b.selector and a.selector < b.selector
147 | or #a.selector > #b.selector
148 | end)
149 |
150 | for i,info in ipairs(CONF.handlers) do
151 | if i < #CONF.handlers and info.selector == CONF.handlers[i+1].selector then
152 | syslog('warning',"duplicate selector %q found",info.selector)
153 | io.stderr:write(string.format("duplicate selector %q found",info.selector),"\n")
154 | end
155 | loadmodule(info)
156 | end
157 | end
158 |
159 | local mklink = require "port70.mklink" -- XXX hack
160 |
161 | -- ************************************************************************
162 |
163 | local redirect_subst do
164 | local replace = lpeg.C(lpeg.P"$" * lpeg.R"09") * lpeg.Carg(1)
165 | / function(c,t)
166 | c = tonumber(c:sub(2,-1))
167 | return t[c]
168 | end
169 | local char = replace + lpeg.P(1)
170 | redirect_subst = lpeg.Cs(char^1)
171 | end
172 |
173 | local function redirect(ios,selector)
174 | for _,rule in ipairs(CONF.redirect.permanent) do
175 | local match = table.pack(selector:match(rule[1]))
176 | if #match > 0 then
177 | ios:write(mklink {
178 | type = 'error',
179 | display = "Permanent redirect",
180 | selector = redirect_subst:match(rule[2],1,match)
181 | })
182 | return true
183 | end
184 | end
185 |
186 | for _,pattern in ipairs(CONF.redirect.gone) do
187 | if selector:match(pattern) then
188 | ios:write(mklink {
189 | type = 'error',
190 | display = "Gone",
191 | selector = selector
192 | })
193 | return true
194 | end
195 | end
196 | end
197 |
198 | -- ************************************************************************
199 |
200 | local parserequest = lpeg.C(lpeg.R" ~"^0)
201 | * (lpeg.P"\t" * lpeg.C(lpeg.R" ~"^1))^-1
202 | * lpeg.P(-1)
203 | + lpeg.Cc(nil)
204 |
205 | local function main(ios)
206 | local request = ios:read("*l")
207 | if not request then
208 | syslog(
209 | 'info',
210 | "remote=%s status=false request=%q bytes=%d",
211 | ios.__remote.addr,
212 | "",
213 | 0
214 | )
215 | ios:close()
216 | return
217 | end
218 |
219 | local selector,search = parserequest:match(request)
220 | local binary = false
221 | local okay = false
222 | local found = false
223 |
224 | if selector then
225 | if redirect(ios,selector) then
226 | found = true -- but it's been moved, or it's gone
227 | else
228 | for _,info in ipairs(CONF.handlers) do
229 | if selector:sub(1,#info.selector) == info.selector then
230 | found = true
231 | local req =
232 | {
233 | selector = info.selector,
234 | rest = selector:sub(#info.selector + 1,-1),
235 | search = search,
236 | remote = ios.__remote,
237 | }
238 |
239 | okay,binary = info.code.handler(info,req,ios)
240 | break
241 | end
242 | end
243 | end
244 | else
245 | ios:write(mklink { type = 'error' , display = "Bad request" , selector = selector })
246 | end
247 |
248 | if not found then
249 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = selector })
250 | end
251 |
252 | if not binary then
253 | ios:write(".\r\n")
254 | end
255 |
256 | ios:close()
257 | syslog(
258 | 'info',
259 | "remote=%s status=%s request=%q bytes=%d",
260 | ios.__remote.addr,
261 | tostring(okay),
262 | request,
263 | ios.__wbytes
264 | )
265 | end
266 |
267 | -- ************************************************************************
268 |
269 | local okay,err = tcp.listen(CONF.network.addr,CONF.network.port,main)
270 |
271 | if not okay then
272 | io.stderr:write(string.format("%s: %s\n",arg[1],err))
273 | syslog('error',"%s: %s",arg[1],err)
274 | os.exit(exit.OSERR,true)
275 | end
276 |
277 | if not setugid(CONF.user) then
278 | os.exit(exit.CONF,true)
279 | end
280 |
281 | signal.catch('int')
282 | signal.catch('term')
283 | syslog('info',"entering service")
284 |
285 | nfl.server_eventloop(function() return signal.caught() end)
286 |
287 | for _,info in ipairs(CONF.handlers) do
288 | if info.fini then
289 | local ok,status = pcall(info.code.fini,info)
290 | if not ok then
291 | syslog('error',"%s: %s",info.module,status)
292 | end
293 | end
294 | end
295 |
296 | os.exit(true,true)
297 |
--------------------------------------------------------------------------------
/port70/cgi.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Common Gateway Interface (CGI)
4 | -- Copyright 2020 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: ignore 611
23 | -- RFC-3875, with some deviations for gopher
24 |
25 | local syslog = require "org.conman.syslog"
26 | local errno = require "org.conman.errno"
27 | local fsys = require "org.conman.fsys"
28 | local process = require "org.conman.process"
29 | local exit = require "org.conman.const.exit"
30 | local mkios = require "org.conman.net.ios"
31 | local nfl = require "org.conman.nfl"
32 | local mklink = require "port70.mklink"
33 | local io = require "io"
34 | local coroutine = require "coroutine"
35 |
36 | local require = require
37 | local pairs = pairs
38 | local ipairs = ipairs
39 |
40 | local DEVNULI = io.open("/dev/null","r")
41 | local DEVNULO = io.open("/dev/null","w")
42 |
43 | -- ************************************************************************
44 |
45 | local function fdtoios(fd)
46 | local newfd = mkios()
47 | newfd.__fd = fd
48 | newfd.__co = coroutine.running()
49 |
50 | newfd.close = function(self)
51 | nfl.SOCKETS:remove(fd)
52 | self.__fd:close()
53 | return true
54 | end
55 |
56 | newfd._refill = function()
57 | return coroutine.yield()
58 | end
59 |
60 | nfl.SOCKETS:insert(fd,'r',function(event)
61 | if event.read then
62 | local data,err = fd:read(8192)
63 | if data then
64 | if #data == 0 then
65 | nfl.SOCKETS:remove(fd)
66 | newfd._eof = true
67 | end
68 | nfl.schedule(newfd.__co,data)
69 | else
70 | if err ~= errno.EAGAIN then
71 | syslog('error',"fd:read() = %s",errno[err])
72 | end
73 | end
74 | else
75 | newfd._eof = true
76 | nfl.SOCKETS:remove(fd)
77 | nfl.schedule(newfd.__co)
78 | end
79 | end)
80 |
81 | return newfd
82 | end
83 |
84 | -- ************************************************************************
85 |
86 | return function(program,cinfo,request,ios)
87 | local conf = require "port70.CONF"
88 |
89 | if not conf.cgi then
90 | syslog('error',"CGI script %q called, but CGI not configured!",program)
91 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
92 | return false
93 | end
94 |
95 | local pipe,err1 = fsys.pipe()
96 | if not pipe then
97 | syslog('error',"CGI pipe: %s",errno[err1])
98 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
99 | return false
100 | end
101 |
102 | pipe.read:setvbuf('no') -- buffering kills the event loop
103 |
104 | local child,err2 = process.fork()
105 |
106 | if not child then
107 | syslog('error',"process.fork() = %s",errno[err2])
108 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
109 | return false
110 | end
111 |
112 | -- =========================================================
113 | -- The child runs off to do its own thang ...
114 | -- =========================================================
115 |
116 | if child == 0 then
117 | fsys.redirect(DEVNULI,io.stdin);
118 | fsys.redirect(pipe.write,io.stdout);
119 | fsys.redirect(DEVNULO,io.stderr);
120 |
121 | -- -----------------------------------------------------------------
122 | -- Close file descriptors that aren't stdin, stdout or stderr. Most
123 | -- Unix systems have dirfd(), right? Right? And /proc/self/fd,
124 | -- right? Um ... erm ...
125 | -- -----------------------------------------------------------------
126 |
127 | local dir = fsys.opendir("/proc/self/fd")
128 | if dir and dir._tofd then
129 | local dirfh = dir:_tofd()
130 |
131 | for file in dir.next,dir do
132 | local fh = tonumber(file)
133 | if fh > 2 and fh ~= dirfh then
134 | fsys._close(fh)
135 | end
136 | end
137 |
138 | -- ----------------------------------------------------------
139 | -- if all else fails, at least close these to make this work
140 | -- ----------------------------------------------------------
141 |
142 | else
143 | DEVNULI:close()
144 | DEVNULO:close()
145 | pipe.write:close()
146 | pipe.read:close()
147 | end
148 |
149 | local cwd = conf.cgi.cwd
150 | local no_slash = conf.cgi.no_slash
151 | local args = {}
152 | local env = {}
153 |
154 | if conf.cgi.env then
155 | for var,val in pairs(conf.cgi.env) do
156 | env[var] = val
157 | end
158 | end
159 |
160 | if conf.cgi.instance then
161 | for name,info in pairs(conf.cgi.instance) do
162 | if request.rest:match(name) then
163 | if info.cwd then cwd = info.cwd end
164 |
165 | -- ---------------------------------------------------------------
166 | -- We want an instance no_slash to override a global no_slash, but
167 | -- if we can't just simply assign no_slash to the instance
168 | -- no_slash because if it doesn't exist, then it will set no_slash
169 | -- to false, possibly overriding the global var, which is NOT what
170 | -- is wanted here. We need to check if the instance no_slash
171 | -- exists to properly override it.
172 | -- ---------------------------------------------------------------
173 |
174 | if type(info.no_slash) == 'boolean' then
175 | no_slash = info.no_slash
176 | end
177 |
178 | if info.arg then
179 | for i,arg in ipairs(info.arg) do
180 | args[i] = arg
181 | end
182 | end
183 |
184 | if info.env then
185 | for var,val in pairs(info.env) do
186 | env[var] = val
187 | end
188 | end
189 | end
190 | end
191 | end
192 |
193 | local _,e = program:find(cinfo.directory,1,true)
194 | local script = e and program:sub(e+1,-1) or program
195 |
196 | if no_slash and script:match("^/") then
197 | script = script:sub(2,-1)
198 | end
199 |
200 | env.GOPHER_DOCUMENT_ROOT = cinfo.directory
201 | env.GOPHER_SCRIPT_FILENAME = program
202 | env.GOPHER_SELECTOR = request.selector .. request.rest
203 | env.GATEWAY_INTERFACE = "CGI/1.1"
204 | env.QUERY_STRING = request.search or ""
205 | env.REMOTE_ADDR = request.remote.addr
206 | env.REMOTE_HOST = request.remote.addr
207 | env.REQUEST_METHOD = ""
208 | env.SCRIPT_NAME = request.selector .. "/" .. script
209 | env.SERVER_NAME = conf.network.host
210 | env.SERVER_PORT = conf.network.port
211 | env.SERVER_PROTOCOL = "GOPHER"
212 | env.SERVER_SOFTWARE = "port70"
213 |
214 | _,e = request.rest:find(fsys.basename(program),1,true)
215 | local pathinfo = e and request.rest:sub(e+1,-1) or request.selector
216 | if pathinfo ~= "" then
217 | env.PATH_TRANSLATED = env.GOPHER_DOCUMENT_ROOT .. request.rest
218 | pathinfo = pathinfo .. request.rest
219 |
220 | if no_slash and pathinfo:match("^/") then
221 | pathinfo = pathinfo:sub(2,-1)
222 | end
223 |
224 | env.PATH_INFO = pathinfo
225 | end
226 |
227 | if cwd then
228 | local okay,err3 = fsys.chdir(cwd)
229 | if not okay then
230 | syslog('error',"CGI cwd(%q) = %s",cwd,errno[err3])
231 | process.exit(exit.CONFIG)
232 | end
233 | end
234 |
235 | process.exec(program,args,env)
236 | process.exit(exit.OSERR)
237 | end
238 |
239 | -- =========================================================
240 | -- Meanwhile, back at the parent's place ...
241 | --
242 | -- NOTE: the CGI script is reponsible for sending the final '.' if the
243 | -- output is text.
244 | -- =========================================================
245 |
246 | pipe.write:close()
247 | local inp = fdtoios(pipe.read)
248 | repeat
249 | local data = inp:read(1024)
250 | if data then ios:write(data) end
251 | until not data
252 | inp:close()
253 |
254 | local info,err4 = process.wait(child)
255 |
256 | if not info then
257 | syslog('error',"process.wait() = %s",errno[err4])
258 | return true,true
259 | end
260 |
261 | if info.status == 'normal' then
262 | if info.rc == 0 then
263 | return true,true
264 | else
265 | syslog('warning',"program=%q status=%d",program,info.rc)
266 | return true,true
267 | end
268 | else
269 | syslog('error',"program=%q status=%s description=%s",program,info.status,info.description)
270 | return true,true
271 | end
272 | end
273 |
--------------------------------------------------------------------------------
/port70/getuserdir.c:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | *
3 | * Get user home directory from the system.
4 | * Copyright 2019 by Sean Conner.
5 | *
6 | * This library is free software; you can redistribute it and/or modify it
7 | * under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or (at your
9 | * option) any later version.
10 | *
11 | * This library is distributed in the hope that it will be useful, but
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 | * License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this library; if not, see .
18 | *
19 | * Comments, questions and criticisms can be sent to: sean@conman.org
20 | *
21 | *************************************************************************/
22 |
23 | #include
24 | #include
25 |
26 | #include
27 | #include
28 |
29 | static int getuserdir(lua_State *L)
30 | {
31 | struct passwd *ent = getpwnam(luaL_checkstring(L,1));
32 | if (ent != NULL)
33 | lua_pushstring(L,ent->pw_dir);
34 | else
35 | lua_pushnil(L);
36 | return 1;
37 | }
38 |
39 | int luaopen_port70_getuserdir(lua_State *L)
40 | {
41 | lua_pushcfunction(L,getuserdir);
42 | return 1;
43 | }
44 |
--------------------------------------------------------------------------------
/port70/handlers/content.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- The sample handler
4 | -- Copyright 2021 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611
24 |
25 | _ENV = {}
26 |
27 | -- ************************************************************************
28 |
29 | function init(conf)
30 | if not conf.content then
31 | return false,"missing content"
32 | end
33 |
34 | return true
35 | end
36 |
37 | -- ************************************************************************
38 |
39 | function handler(conf,_,ios)
40 | ios:write(conf.content)
41 | return true
42 | end
43 |
44 | -- ************************************************************************
45 |
46 | return _ENV
47 |
--------------------------------------------------------------------------------
/port70/handlers/file.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- File handler
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611
24 |
25 | local lpeg = require "lpeg"
26 | local readfile = require "port70.readfile"
27 |
28 | _ENV = {}
29 |
30 | -- ************************************************************************
31 |
32 | local extension do
33 | local char = lpeg.C(lpeg.S"^$()%.[]*+-?") / "%%%1"
34 | + lpeg.R" \255"
35 | extension = lpeg.Cs(char^1 * lpeg.Cc"$")
36 | end
37 |
38 | -- ************************************************************************
39 |
40 | function init(conf)
41 | if not conf.extension then
42 | conf.extension = "%.port70$"
43 | else
44 | conf.extension = extension:match(conf.extension)
45 | end
46 |
47 | if not conf.file then
48 | return false,"missing file specification"
49 | else
50 | return true
51 | end
52 | end
53 |
54 | -- ************************************************************************
55 |
56 | function handler(info,request,ios)
57 | return readfile(info.file,info.extension,info,request,ios)
58 | end
59 |
60 | -- ************************************************************************
61 |
62 | return _ENV
63 |
--------------------------------------------------------------------------------
/port70/handlers/filesystem.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Filesystem handler
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611 631
24 |
25 | local mimetype = require "org.conman.parsers.mimetype"
26 | local magic = require "org.conman.fsys.magic"
27 | local gtypes = require "org.conman.const.gopher-types"
28 | local syslog = require "org.conman.syslog"
29 | local errno = require "org.conman.errno"
30 | local fsys = require "org.conman.fsys"
31 | local readfile = require "port70.readfile"
32 | local mklink = require "port70.mklink"
33 | local lpeg = require "lpeg"
34 | local table = require "table"
35 | local ipairs = ipairs
36 | local type = type
37 | local assert = assert
38 |
39 | _ENV = {}
40 |
41 | -- ************************************************************************
42 |
43 | local function descend_path(path)
44 | local function iter(state,var)
45 | local n = state()
46 | if n then
47 | return var .. "/" .. n,n
48 | end
49 | end
50 |
51 | return iter,path:gmatch("[^/]*"),"."
52 | end
53 |
54 | -- ************************************************************************
55 |
56 | local function deny(no_access,segment)
57 | if no_access then
58 | for _,pattern in ipairs(no_access) do
59 | if segment:match(pattern) then
60 | return true
61 | end
62 | end
63 | end
64 | end
65 |
66 | -- ************************************************************************
67 |
68 | local extension do
69 | local char = lpeg.C(lpeg.S"^$()%.[]*+-?") / "%%%1"
70 | + lpeg.R" \255"
71 | extension = lpeg.Cs(char^1 * lpeg.Cc"$")
72 | end
73 |
74 | -- ************************************************************************
75 |
76 | function init(conf)
77 | if not conf.index then
78 | conf.index = { "index.port70" , "index.gopher" }
79 | elseif type(conf.index) == 'string' then
80 | conf.index = { conf.index }
81 | end
82 |
83 | if not conf.extension then
84 | conf.extension = "%.port70$"
85 | else
86 | conf.extension = extension:match(conf.extension)
87 | end
88 |
89 | if not conf.no_access then
90 | conf.no_access = { "^%." }
91 | end
92 |
93 | if not conf.dirext then
94 | conf.dirext = { "%.port70$" , "%.gopher$" }
95 | elseif type(conf.dirext) == 'string' then
96 | conf.dirext = { extension:match(conf.dirext) }
97 | elseif type(conf.dirext) == 'table' then
98 | for i = 1 , #conf.dirext do
99 | conf.dirext[i] = extension:match(conf.dirext[i])
100 | end
101 | end
102 |
103 | return true
104 | end
105 |
106 | -- ************************************************************************
107 |
108 | local function gophertype(filename)
109 | local mime = mimetype:match(magic(filename))
110 |
111 | if not mime then
112 | syslog('warning',"%s: no mime type",filename)
113 | return gtypes.binary
114 | end
115 |
116 | if mime.type:match "^text/html" then
117 | return 'html'
118 | elseif mime.type:match "^text/" then
119 | return 'file'
120 | elseif mime.type:match "^image/gif" then
121 | return 'gif'
122 | elseif mime.type:match "^image/" then
123 | return 'image'
124 | elseif mime.type:match "^audio/" then
125 | return 'sound'
126 | else
127 | return 'binary'
128 | end
129 | end
130 |
131 | -- ************************************************************************
132 |
133 | function handler(info,request,ios)
134 | assert(ios)
135 | assert(ios.write)
136 | assert(ios._drain)
137 | local directory = info.directory
138 | local sep = ""
139 |
140 | local selector = info.selector
141 |
142 | for _,segment in descend_path(request.rest) do
143 | if deny(info.no_access,segment) then
144 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector .. request.rest })
145 | return false
146 | end
147 |
148 | directory = directory .. "/" .. segment
149 | selector = selector .. sep .. segment
150 | sep = "/"
151 |
152 | local finfo,err1 = fsys.stat(directory)
153 |
154 | if not finfo then
155 | syslog('error',"stat(%q) = %s",directory,errno[err1])
156 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector .. request.rest })
157 | return false
158 | end
159 |
160 | if finfo.mode.type == 'dir' then
161 | if not fsys.access(directory,"x") then
162 | syslog('error',"access(%q) failed",directory)
163 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector .. request.rest })
164 | return false
165 | end
166 | elseif finfo.mode.type == 'file' then
167 | return readfile(directory,info.extension,info,request,ios)
168 | else
169 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector .. request.rest })
170 | return false
171 | end
172 | end
173 |
174 | for _,index in ipairs(info.index) do
175 | if fsys.access(directory .. "/" .. index,"r") then
176 | return readfile(directory .. "/" .. index,info.extension,info,request,ios)
177 | end
178 | end
179 |
180 | local links = {}
181 |
182 | local function is_index_file(name)
183 | for _,ext in ipairs(info.dirext) do
184 | if name:match(ext) then
185 | return true
186 | end
187 | end
188 | end
189 |
190 | for file in fsys.dir(directory) do
191 | if not deny(info.no_access,file) then
192 | local finfo = fsys.stat(directory .. "/" .. file)
193 | if finfo then
194 | if finfo.mode.type == 'file' then
195 | if is_index_file(file) then
196 | table.insert(links,{ type = 'dir' , selector = selector .. file , display = file })
197 | else
198 | local gtype = gophertype(directory .. '/' .. file)
199 | table.insert(links,{ type = gtype , selector = selector .. file , display = file })
200 | end
201 | elseif finfo.mode.type == 'dir' then
202 | table.insert(links,{ type = 'dir' , selector = selector .. file .. '/', display = file })
203 | end
204 | end
205 | end
206 | end
207 |
208 | table.sort(links,function(a,b)
209 | if a.type == 'dir' and b.type ~= 'dir' then
210 | return true
211 | elseif a.type ~= 'dir' and b.type == 'dir' then
212 | return false
213 | end
214 |
215 | return a.selector < b.selector
216 | end)
217 |
218 | for _,link in ipairs(links) do
219 | ios:write(mklink(link))
220 | end
221 |
222 | return true
223 | end
224 |
225 | -- ************************************************************************
226 |
227 | return _ENV
228 |
--------------------------------------------------------------------------------
/port70/handlers/http.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- File handler
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611
24 |
25 | local string = require "string"
26 | local os = require "os"
27 |
28 | _ENV = {}
29 |
30 | -- ************************************************************************
31 |
32 | local header = "HTTP/1.1 418 I'm a teapot\r\n"
33 | .. "Date: %s\r\n"
34 | .. "Server: NOT A WEBSERVER!\r\n"
35 | .. "Connection: close\r\n"
36 | .. "Content-Type: text/plain; charset=US-ASCII\r\n"
37 | .. "Content-Length: %d\r\n"
38 | .. "\r\n"
39 | local document = "I'm a little teapot\r\n"
40 | .. "Short and stout\r\n"
41 | .. "Here is my handle\r\n"
42 | .. "Here is my spout\r\n"
43 | .. "\r\n"
44 | .. "When I get all steamed up\r\n"
45 | .. "Here me shout\r\n"
46 | .. '"Tip me over\r\n'
47 | .. 'and pour me out!"\r\n'
48 | .. "\r\n"
49 | .. "I'm a clever teapot\r\n"
50 | .. "Yes it's true\r\n"
51 | .. "Here let me show you\r\n"
52 | .. "What I can do\r\n"
53 | .. "\r\n"
54 | .. "I can change my handle\r\n"
55 | .. "into a spout\r\n"
56 | .. "Tip me over\r\n"
57 | .. "and pour me out!\r\n"
58 | .. "\r\n"
59 | .. "P.S. I'm not really a teapot.\r\n"
60 | .. " I'm a gopher server.\r\n"
61 | .. " Get with the times.\r\n"
62 | .. "\r\n"
63 |
64 | -- ***********************************************************************
65 |
66 | function handler(_,_,ios)
67 | repeat local line = ios:read("l") until line == ""
68 | local hdr = string.format(header,os.date("!%a, %d %b %Y %H:%M:%S GMT"),#document)
69 | ios:write(hdr,document)
70 | return true,true
71 | end
72 |
73 | -- ***********************************************************************
74 |
75 | return _ENV
76 |
--------------------------------------------------------------------------------
/port70/handlers/sample.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- The sample handler
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler fini
23 | -- luacheck: ignore 611
24 |
25 | local syslog = require "org.conman.syslog"
26 | local string = require "string"
27 | local tostring = tostring
28 |
29 | _ENV = {}
30 |
31 | -- ************************************************************************
32 | -- Usage: okay[,err] = sample.init(conf)
33 | -- Desc: Do any initialization of the module
34 | -- Input: conf (table) configuration block from configuration file
35 | -- Return: okay (boolean) true if okay, false if any error
36 | -- err (string) error message
37 | --
38 | -- NOTE: This function is optional.
39 | -- Also, any information local to an instance can be stored
40 | -- in the passed in configuration block.
41 | -- ************************************************************************
42 |
43 | function init(conf)
44 | syslog('debug',"%s: selector=%q",conf.module,conf.selector)
45 | return true
46 | end
47 |
48 | -- ************************************************************************
49 | -- Usage: okay[,binary] = sample.handler(conf,request,ios)
50 | -- Desc: Return content for a given selector
51 | -- Input: conf (table) configuration block
52 | -- request (table) request---table with following fields:
53 | -- * selector (string) requested selector
54 | -- * rest (string) text after requested selector
55 | -- * search (string) search string (if any)
56 | -- ios (table/object) input/output stream
57 | -- Return: okay (boolean) true if okay, false if error
58 | -- binary (boolean/optional) true if output is binary,
59 | -- | false if text
60 | -- ************************************************************************
61 |
62 | function handler(conf,request,ios)
63 | ios:write(string.format([[
64 | conf.module=%q
65 | conf.selector=%q
66 | selector=%q
67 | rest=%q
68 | search=%q"
69 | remote=%s
70 | ]],
71 | conf.module,
72 | conf.selector,
73 | request.selector,
74 | request.rest,
75 | request.search or "",
76 | tostring(ios.__remote)
77 | ))
78 | return true
79 | end
80 |
81 | -- ************************************************************************
82 | -- Usage: okay[,err] = sample.fini(conf)
83 | -- Desc: Cleanup resources for module
84 | -- Input: conf (table) configuration block
85 | -- Return: okay (boolean) true if okay, false otherwise
86 | -- err (string/optional) error message
87 | --
88 | -- NOTE: This function is optional.
89 | -- ************************************************************************
90 |
91 | function fini(conf)
92 | syslog('debug',"fini(%s) selector pattern=%q",conf.module,conf.selector)
93 | return true
94 | end
95 |
96 | -- ************************************************************************
97 |
98 | return _ENV
99 |
--------------------------------------------------------------------------------
/port70/handlers/url.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- URL handler (https://tools.ietf.org/html/draft-matavka-gopher-ii-03)
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611
24 |
25 | _ENV = {}
26 |
27 | local document =
28 | [[
29 |
30 |
31 |
32 |
33 |
34 |
35 |
You are following an external link to a Web site. You will be
36 | automatically taken to the site shortly. If you do not get sent there,
37 | please click here to go to the web
38 | site.
39 |
40 |
Thanks for using Gopher!
45 |
46 |
47 |
48 | ]]
49 |
50 | function handler(_,request,ios)
51 | ios:write((document:gsub("LINK",request.rest)))
52 | return true
53 | end
54 |
55 | return _ENV
56 |
--------------------------------------------------------------------------------
/port70/handlers/userdir.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Handle user directories
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals init handler
23 | -- luacheck: ignore 611
24 |
25 | local fsys = require "org.conman.fsys"
26 | local filesystem = require "port70.handlers.filesystem"
27 | local getuserdir = require "port70.getuserdir"
28 |
29 | _ENV = {}
30 |
31 | -- ************************************************************************
32 |
33 | function init(conf)
34 | if not conf.directory then
35 | conf.directory = "public_gopher"
36 | end
37 |
38 | filesystem.init(conf)
39 | return true
40 | end
41 |
42 | -- ************************************************************************
43 |
44 | function handler(conf,request,ios)
45 | local user,path = request.rest:match("^([^/]+)(.*)")
46 | if not user or user == "" then
47 | return false,"Not found"
48 | end
49 |
50 | local userdir = getuserdir(user)
51 | if not userdir then
52 | return false,"Not found"
53 | end
54 |
55 | userdir = userdir .. "/" .. conf.directory
56 |
57 | if not fsys.access(userdir,"rx") then
58 | return false,"Not found"
59 | end
60 |
61 | local fsconf =
62 | {
63 | path = conf.path,
64 | selector = conf.selector .. user,
65 | module = "port70.handlers.filesystem",
66 | directory = userdir,
67 | index = conf.index,
68 | extension = conf.extension,
69 | dirext = conf.dirext,
70 | no_access = conf.no_access,
71 | }
72 |
73 | request.selector = fsconf.selector
74 | request.rest = path
75 | return filesystem.handler(fsconf,request,ios)
76 | end
77 |
78 | -- ************************************************************************
79 |
80 | return _ENV
81 |
--------------------------------------------------------------------------------
/port70/mklink.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Generate a Gopher link (selector line)
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: ignore 611
23 |
24 | local gtypes = require "org.conman.const.gopher-types"
25 | local string = require "string"
26 | local CONF = require "port70.CONF"
27 | local safetext = require "port70.safetext"
28 |
29 | return function(info)
30 | local display = info.display and safetext:match(info.display,1,{})
31 | or ""
32 |
33 | return string.format(
34 | "%s%s\t%s\t%s\t%d\r\n",
35 | gtypes[info.type] or gtypes.file,
36 | display,
37 | info.selector or "",
38 | info.host or CONF.network.host,
39 | info.port or CONF.network.port
40 | )
41 | end
42 |
--------------------------------------------------------------------------------
/port70/readfile.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Read a file just prior to sending it
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: ignore 611
23 |
24 | local syslog = require "org.conman.syslog"
25 | local mimetype = require "org.conman.parsers.mimetype"
26 | local fsys = require "org.conman.fsys"
27 | local magic = require "org.conman.fsys.magic"
28 | local url = require "org.conman.parsers.url.gopher"
29 | + require "org.conman.parsers.url"
30 | local mklink = require "port70.mklink"
31 | local cgi = require "port70.cgi"
32 | local lpeg = require "lpeg"
33 | local io = require "io"
34 | local table = require "table"
35 |
36 | local require = require
37 | local type = type
38 | local assert = assert
39 | local pcall = pcall
40 |
41 | -- ************************************************************************
42 |
43 | local parseline do
44 | local Cc = lpeg.Cc
45 | local C = lpeg.C
46 | local P = lpeg.P
47 | local R = lpeg.R
48 |
49 | local entry = P"\t"
50 | * C(R" ~"^1) * P"\t"^1 -- type
51 | * C(R" ~"^1) * P"\t"^1 -- selector
52 | * C(R" \255"^0) -- display
53 |
54 | local code = P"\t"
55 | * C"Lua{" -- type
56 | * Cc"" -- selector
57 | * Cc"" -- display
58 |
59 | local info = Cc"info" -- type
60 | * Cc"" -- selector
61 | * C(R" \255"^0) -- display
62 |
63 | parseline = entry + code + info
64 | end
65 |
66 | local cleanpath do
67 | local char = lpeg.P"/"^1 / "/"
68 | + lpeg.P(1)
69 | cleanpath = lpeg.Cs(char^0)
70 | end
71 |
72 | -- ************************************************************************
73 |
74 | local function execblock(name,file,ios)
75 | local acc = {}
76 |
77 | repeat
78 | local line = file:read("*L")
79 | table.insert(acc,line)
80 | until line:match "}Lua"
81 |
82 | table.remove(acc)
83 |
84 | local code = table.concat(acc," ")
85 | local env = { require = require }
86 | local f,err = load(code,name,"t",env)
87 |
88 | if not f then
89 | syslog('error',"%s: %s",name,err)
90 | ios:write(mklink {
91 | type = 'info',
92 | display = "Nothing in particular right now"
93 | })
94 | else
95 | local okay,data = pcall(f)
96 | if not okay then
97 | syslog('error',"name=%q err=%q",name,data)
98 | else
99 | if type(data) == 'table' then
100 | ios:write(table.concat(data,"\r\n"))
101 | else
102 | ios:write(data)
103 | end
104 | end
105 | end
106 | end
107 |
108 | -- ************************************************************************
109 |
110 | return function(filename,ext,info,request,ios)
111 | assert(ios)
112 | assert(ios.write)
113 | assert(ios._drain)
114 |
115 | filename = cleanpath:match(filename)
116 |
117 | if not filename then
118 | syslog('warning',"readfile() bad cleanpath")
119 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
120 | return false
121 | end
122 |
123 | if info.cgi and fsys.access(filename,"rx") then
124 | return cgi(filename,info,request,ios)
125 | end
126 |
127 | if filename:match(ext) then
128 | local file = io.open(filename,"r")
129 | if not file then
130 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
131 | return false
132 | end
133 |
134 | for line in file:lines() do
135 | local gtype,selector,display = parseline:match(line)
136 | if gtype == 'url' then
137 | local uri = url:match(selector)
138 | if uri.scheme == 'gopher' then
139 | uri.display = display
140 | ios:write(mklink(uri))
141 | else
142 | ios:write(mklink {
143 | type = 'html',
144 | display = display,
145 | selector = "URL:" .. selector
146 | })
147 | end
148 | elseif gtype == 'Lua{' then
149 | execblock(filename,file,ios)
150 | else
151 | ios:write(mklink {
152 | type = gtype,
153 | display = display,
154 | selector = selector
155 | })
156 | end
157 | end
158 |
159 | file:close()
160 | return true
161 | end
162 |
163 | local mime = mimetype:match(magic(filename))
164 |
165 | if not mime then
166 | syslog('error',"%s: failed to get MIME type",filename)
167 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
168 | return false
169 |
170 | elseif mime.type:match "^text/" then
171 | local file,err = io.open(filename,"r")
172 | if not file then
173 | syslog('error',"io.open(%q) = %s",filename,err)
174 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
175 | return false
176 | end
177 |
178 | for line in file:lines() do
179 | ios:write(line,"\r\n")
180 | end
181 |
182 | file:close()
183 | return true
184 |
185 | else
186 | local file,err = io.open(filename,"rb")
187 | if not file then
188 | syslog('error',"io.open(%q) = %s",filename,err)
189 | ios:write(mklink { type = 'error' , display = "Selector not found" , selector = request.selector })
190 | return false
191 | end
192 |
193 | repeat
194 | local data = file:read(1024)
195 | if data then
196 | ios:write(data)
197 | end
198 | until not data
199 |
200 | file:close()
201 | return true,true
202 | end
203 | end
204 |
--------------------------------------------------------------------------------
/port70/safetext.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Filter out control characters from text.
4 | -- Copyright 2018 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: ignore 611
23 |
24 | local lpeg = require "lpeg" -- semver: ~1.0.0
25 | local utf8 = require "org.conman.parsers.utf8.char"
26 | + require "org.conman.parsers.ascii.char"
27 | local control = require "org.conman.parsers.utf8.control"
28 | + require "org.conman.parsers.iso.control"
29 | + require "org.conman.parsers.ascii.control"
30 |
31 | local Carg = lpeg.Carg
32 | local Cs = lpeg.Cs
33 | local C = lpeg.C
34 | local P = lpeg.P
35 | local R = lpeg.R
36 |
37 | local nc = P"\204" * R"\128\191" -- combining chars
38 | + P"\205" * R"\128\175" -- combining chars
39 | + P"\225\170" * R"\176\190" -- combining chars
40 | + P"\225\183" * R"\128\191" -- combining chars
41 | + P"\226\131" * R"\144\176" -- combining chars
42 | + P"\239\184" * R"\160\175" -- combining chars
43 | + P"\u{00AD}" -- shy hyphen
44 | + P"\u{1806}" -- Mongolian TODO soft hyphen
45 | + P"\u{200B}" -- zero width space
46 | + P"\u{200C}" -- zero-width nonjoiner space
47 | + P"\u{200D}" -- zero-width joiner space
48 |
49 | local c = P"\9" * Carg(1) -- HT
50 | / function(s)
51 | local rem = 8 - (s.cnt % 8)
52 | s.cnt = s.cnt + rem
53 | return string.rep(' ',rem)
54 | end
55 | + control / ""
56 | + nc -- not counted for tabulation
57 | + C(utf8) * Carg(1) -- counted for tabulation
58 | / function(c,s)
59 | s.cnt = s.cnt + 1
60 | return c
61 | end
62 | + P(1) / ""
63 |
64 | return Carg(1) / function(s) s.cnt = 0 end
65 | * Cs(c^0)
66 | * Carg(1) / function(ch,s) return ch,s.cnt end
67 |
--------------------------------------------------------------------------------
/port70/setugid.c:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | *
3 | * Switch userid.
4 | * Copyright 2019 by Sean Conner.
5 | *
6 | * This library is free software; you can redistribute it and/or modify it
7 | * under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or (at your
9 | * option) any later version.
10 | *
11 | * This library is distributed in the hope that it will be useful, but
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 | * License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this library; if not, see .
18 | *
19 | * Comments, questions and criticisms can be sent to: sean@conman.org
20 | *
21 | *************************************************************************/
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #include
34 | #include
35 |
36 | /*************************************************************************/
37 |
38 | static inline int setugidok(lua_State *L,char const *msg)
39 | {
40 | syslog(LOG_NOTICE,"%s",msg);
41 | lua_pushboolean(L,true);
42 | return 1;
43 | }
44 |
45 | /*************************************************************************/
46 |
47 | static inline int setugiderr(lua_State *L,char const *msg)
48 | {
49 | syslog(LOG_ERR,"%s: %s",msg,strerror(errno));
50 | lua_pushboolean(L,false);
51 | return 1;
52 | }
53 |
54 | /*************************************************************************/
55 |
56 | static int setugid(lua_State *L)
57 | {
58 | uid_t uid;
59 | gid_t gid;
60 | int id;
61 | bool fgid;
62 |
63 | if (getuid() != 0)
64 | return setugidok(L,"Not running as root---can't switch userid");
65 |
66 | if (lua_type(L,1) == LUA_TNIL)
67 | return setugidok(L,"Not electing to switch userid");
68 |
69 | luaL_checktype(L,1,LUA_TTABLE);
70 |
71 | id = lua_getfield(L,1,"gid");
72 |
73 | if (id == LUA_TNUMBER)
74 | {
75 | gid = lua_tointeger(L,-1);
76 | fgid = true;
77 | }
78 | else if (id == LUA_TSTRING)
79 | {
80 | struct group *gr = getgrnam(lua_tostring(L,-1));
81 | if (gr != NULL)
82 | gid = gr->gr_gid;
83 | else
84 | return setugiderr(L,"getgrnam()");
85 | fgid = true;
86 | }
87 | else if (id == LUA_TNIL)
88 | fgid = false;
89 | else
90 | return setugiderr(L,"gid wrong type");
91 |
92 | id = lua_getfield(L,1,"uid");
93 |
94 | if (id == LUA_TNUMBER)
95 | {
96 | uid = lua_tointeger(L,-1);
97 | if (!fgid)
98 | {
99 | struct passwd *pw = getpwuid(uid);
100 | if (pw != NULL)
101 | gid = pw->pw_gid;
102 | else
103 | return setugiderr(L,"getpwuid()");
104 | }
105 | }
106 | else if (id == LUA_TSTRING)
107 | {
108 | struct passwd *pw = getpwnam(lua_tostring(L,-1));
109 | if (pw != NULL)
110 | {
111 | uid = pw->pw_uid;
112 | if (!fgid)
113 | gid = pw->pw_gid;
114 | }
115 | else
116 | return setugiderr(L,"getpwnam()");
117 | }
118 | else if (id == LUA_TNIL)
119 | return setugiderr(L,"missing uid");
120 | else
121 | return setugiderr(L,"uid wrong type");
122 |
123 | if (setresgid(gid,gid,gid) == -1)
124 | return setugiderr(L,"setresgid()");
125 |
126 | if (setresuid(uid,uid,uid) == -1)
127 | return setugiderr(L,"setresuid()");
128 |
129 | return setugidok(L,"successfully switched userid");
130 | }
131 |
132 | /*************************************************************************/
133 |
134 | int luaopen_port70_setugid(lua_State *L)
135 | {
136 | lua_pushcfunction(L,setugid);
137 | return 1;
138 | }
139 |
--------------------------------------------------------------------------------
/sample-conf.lua:
--------------------------------------------------------------------------------
1 | -- ************************************************************************
2 | --
3 | -- Sample config file
4 | -- Copyright 2019 by Sean Conner. All Rights Reserved.
5 | --
6 | -- This program is free software: you can redistribute it and/or modify
7 | -- it under the terms of the GNU General Public License as published by
8 | -- the Free Software Foundation, either version 3 of the License, or
9 | -- (at your option) any later version.
10 | --
11 | -- This program is distributed in the hope that it will be useful,
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | -- GNU General Public License for more details.
15 | --
16 | -- You should have received a copy of the GNU General Public License
17 | -- along with this program. If not, see .
18 | --
19 | -- Comments, questions and criticisms can be sent to: sean@conman.org
20 | --
21 | -- ************************************************************************
22 | -- luacheck: globals network syslog no_access user redirect handlers cgi
23 | -- luacheck: ignore 611
24 |
25 | -- ************************************************************************
26 | -- Network definition block, required (optional fields are default values)
27 | -- ************************************************************************
28 |
29 | network =
30 | {
31 | host = "example.com",
32 | addr = "::", -- optional, default listens all interfaces, IPv4/IPv6
33 | port = 70, -- optional, port to listen on
34 | }
35 |
36 | -- ************************************************************************
37 | -- syslog() definition block, optional (default value presented)
38 | -- ************************************************************************
39 |
40 | syslog =
41 | {
42 | ident = 'gopher',
43 | facility = 'daemon',
44 | }
45 |
46 | -- ************************************************************************
47 | -- User to run under, optional. Either numeric value, or name of user. If
48 | -- not given, userid will not change (you have to be root for this to work
49 | -- anyway ... )
50 | -- ************************************************************************
51 |
52 | user =
53 | {
54 | uid = 'gopher',
55 | gid = 'gopher', -- optional, defaults to group of supplied user
56 | }
57 |
58 | -- ************************************************************************
59 | -- Experimental Redirection for gopher. This replaces the earlier redirect
60 | -- module I had. The new method is a bit more flexible, as it can deal with
61 | -- both permament redirections and selectors that are no longer valid
62 | -- (gone).
63 | --
64 | -- So, before any handlers are checked, requests are filtered through these
65 | -- redirection blocks. The first is the permanent block, then the gone
66 | -- block. The format for the permanent block is a Lua pattern, then a
67 | -- redirection location where "$1" will be replaced with the first capture,
68 | -- "$2" with the second capture and so on. This will return a gopher error
69 | -- with the display string of "Permanent redirect" and the new selector to
70 | -- use.
71 |
72 | -- The gone block is just a pattern to match against; there is no
73 | -- substitution required, as we're not really directing anywhere. This will
74 | -- return a gopher error with the display string of "Gone" with the original
75 | -- selector.
76 | -- ************************************************************************
77 |
78 | redirect =
79 | {
80 | permanent =
81 | {
82 | { "^/oldselector/(.*)" , "/newselector/$1" },
83 | { "^/old/(.*)/(.*)/(.*)" , "/new/$1-$2-$2" }
84 | },
85 |
86 | gone =
87 | {
88 | "^/no%-longer%-here$",
89 | "^/this_is_gone_as_well$",
90 | "^/obsolete(.*)"
91 | }
92 | }
93 |
94 | -- ************************************************************************
95 | -- Handlers, mostly optional
96 | --
97 | -- These handle all requests. The configuration options are entirely
98 | -- dependant upon the handler---the only required configuration options per
99 | -- handler are the 'selector' field and the 'module' field, which defines
100 | -- the codebase for the handler. The selector fields match the *beginning*
101 | -- of the selector; the rest of the selector is then passed to the nandler.
102 | -- The first handler that matches is the one that handles the request.
103 | -- ************************************************************************
104 |
105 | handlers =
106 | {
107 | -- -------------------------------------------------------------------
108 | -- Individual file handler. This allows you to link to a file on the
109 | -- computer---it does not have to appear in a directory served up by
110 | -- the filesystem handler.
111 | -- -------------------------------------------------------------------
112 |
113 | {
114 | selector = "/motd",
115 | module = "port70.handlers.file",
116 | file = "/etc/motd", --required
117 | },
118 |
119 | -- ----------------------------------------------------------------
120 | -- The user directory handler, allowing individual users of the computer
121 | -- to serve up gopher content. The rest of the selector is assumed to be
122 | -- of the form "[^/]+/.*"---that is, the username starts the path, and is
123 | -- delimeted by a '/' character.
124 | --
125 | -- The directory field is the subdirectory underneath the users $HOME that
126 | -- is served up. The index field are a list of files that will be
127 | -- considered "index" files and served up; if one isn't provided, one will
128 | -- be constructed. The extension field gives the exention used for the
129 | -- port70-specific index format (which is different than a traditional
130 | -- gopher index). The no_access field is a list of patterns for each
131 | -- filename that will NOT be served up.
132 | -- --------------------------------------------------------------------------
133 |
134 | {
135 | selector = "/~",
136 | module = "port70.handlers.userdir",
137 | directory = "public_gopher", -- optional
138 | index = { "index.port70" , "index.gopher" }, -- optional
139 | extension = ".port70", -- optional
140 | no_access = -- optional
141 | {
142 | "^%.",
143 | },
144 | },
145 |
146 | -- -----------------------------------------------------------------
147 | -- The content handler---for when a file is just too much overhead.
148 | -- -----------------------------------------------------------------
149 |
150 | {
151 | selector = "/hello.txt",
152 | module = "port70.handlers.content",
153 | content = "Hello, world!\r\n",
154 | },
155 |
156 | -- --------------------------------------------------------------------
157 | -- The HTTP handler---a way to tell those pesky web robots to go away,
158 | -- we aren't a web server but a tea pot.
159 | -- --------------------------------------------------------------------
160 |
161 | { selector = "GET " , module = "port70.handlers.http" },
162 | { selector = "HEAD " , module = "port70.handlers.http" },
163 | { selector = "POST " , module = "port70.handlers.http" },
164 | { selector = "PUT " , module = "port70.handlers.http" },
165 | { selector = "DELETE " , module = "port70.handlers.http" },
166 | { selector = "CONNECT " , module = "port70.handlers.http" },
167 | { selector = "OPTIONS " , module = "port70.handlers.http" },
168 | { selector = "TRACE " , module = "port70.handlers.http" },
169 | { selector = "BREW " , module = "port70.handlers.http" }, -- RFC-2324, in case people get cute
170 | { selector = "PROPFIND " , module = "port70.handlers.http" },
171 | { selector = "WHEN " , module = "port70.handlers.http" },
172 |
173 | -- ---------------------------------------------------------------------
174 | -- URL handler. If a gopher client doesn't understand the URL: marker
175 | -- of the 'h' type, use this to send back an HTML page with a redirect
176 | -- to the given URL.
177 | -- ---------------------------------------------------------------------
178 |
179 | {
180 | selector = "URL:",
181 | module = "port70.handlers.url",
182 | },
183 |
184 | -- --------------------------------------------------------------------
185 | -- The sample handler. This just exists to give you a skeleton of a
186 | -- handler to work from.
187 | -- --------------------------------------------------------------------
188 |
189 | {
190 | selector = "sample/",
191 | module = "port70.handlers.sample",
192 | },
193 |
194 | -- --------------------------------------------------------------------
195 | -- The filesystem handler. You will most likely want to use this one.
196 | -- You are not restricted to a single instance of this. The first example
197 | -- sets up a hypthetical source of tarballs, while the second example will
198 | -- serve up all other selectors that haven't matched by a file.
199 | --
200 | -- The index field is a list of filenames that will be displayed if a
201 | -- directory is specified.
202 | --
203 | -- The dirext field is a list of extensions for files that should have
204 | -- the 'dir' selector type instead of 'file'.
205 | --
206 | -- The extension field is the extension used for special processing
207 | -- of an index file.
208 | --
209 | -- the cgi field enables CGI scripts. You will also need to configure
210 | -- the CGI handler block (see below).
211 | -- -----------------------------------------------------------------------
212 |
213 | {
214 | selector = "/archive",
215 | module = "port70.handlers.filesystem",
216 | directory = "/usr/src/archive", -- required
217 | index = { "index.port70" , "index.gopher" }, -- optional
218 | dirext = { ".port70" , ".gopher" }, -- optional
219 | extension = ".port70", -- optional
220 | cgi = false, -- optional
221 | no_access = -- optional
222 | {
223 | "^%.",
224 | },
225 | },
226 |
227 | {
228 | selector = "", -- matches everything else
229 | module = "port70.handlers.filesystem",
230 | directory = "share", -- required,
231 | index = { "index.port70" , "index.gopher" }, -- optional
232 | extension = ".port70", -- optional
233 | cgi = true, -- optional
234 | no_access = -- optional
235 | {
236 | "^%.",
237 | },
238 | },
239 | }
240 |
241 | -- ************************************************************************
242 | -- CGI definition block, optional
243 | --
244 | -- Any file found with the executable bit set and the cgi field in the
245 | -- handler definition block is true, is considered a CGI script and will be
246 | -- executed as such. This module implements the CGI standard as defined in
247 | -- RFC-3875 with some deviations due to the semantics of gopher. The script
248 | -- will be executed and any output will be sent to the client. The script
249 | -- SHOULD NOT include the standard CGI header output as that does not make
250 | -- semantic sense for gopher. The output SHOULD be what a gopher client is
251 | -- expecting per the selector type. The following environment variables
252 | -- will be defined:
253 | --
254 | -- GATEWAY_INTERFACE Will be set to "CGI/1.1"
255 | -- PATH_INFO May be set (see RFC-3875 for details)
256 | -- PATH_TRANSLATED May be set (see RFC-3875 for details)
257 | -- QUERY_STRING Will be set to the passed in search query, or ""
258 | -- REMOTE_ADDR IP address of the client
259 | -- REMOTE_HOST IP address of the client (allowed in RFC-3875)
260 | -- REQUEST_METHOD Set to "" (not defined for gopher)
261 | -- SCRIPT_NAME Name of the script per the gopher selector
262 | -- SERVER_NAME Per network.host
263 | -- SERVER_PORT Per network.port
264 | -- SERVER_PROTOCOL Set to "GOPHER"
265 | -- SERVER_SOFTWARE Set to "port70"
266 | -- GOPHER_DOCUMENT_ROOT Set to the parent directory of the CGI script
267 | -- GOPHER_SCRIPT_FILENAME Set to the full path of the script
268 | -- GOPHER_SELECTOR Set to the raw selector
269 | --
270 | -- If this block is NOT defined, then no scripts will be run, and any file
271 | -- found that is marked as 'executable' will return "Not found" as a
272 | -- security measure.
273 | -- ************************************************************************
274 |
275 | cgi =
276 | {
277 | -- ----------------------------------------------------------------
278 | -- The following variables apply to ALL CGI scripts. They are all
279 | -- optional, and not not beed to be defined.
280 | -- ----------------------------------------------------------------
281 |
282 | -- ------------------------------------------------------
283 | -- Define to true if you do no want leading slashes--e.g.
284 | -- gopher://example.com/0script
285 | -- If NOT defined, then a leading slash is assumed---e.g.
286 | -- gopher://example.com/0/script
287 | -- ------------------------------------------------------
288 |
289 | no_slash = true,
290 |
291 | -- -----------------------------------------------------------
292 | -- All scripts will use this as the current working directory.
293 | -- -----------------------------------------------------------
294 |
295 | cwd = "/tmp",
296 |
297 | -- ------------------------------------------------------------------
298 | -- Additional environment variables can be set. The following list
299 | -- is probably what would be nice to have.
300 | -- ------------------------------------------------------------------
301 |
302 | env =
303 | {
304 | PATH = "/usr/local/bin:/usr/bin:/bin",
305 | LANG = "en_US.UTF-8",
306 | },
307 |
308 | -- -------------------------------------------------------------------
309 | -- The instance block allows you to define values per CGI script and
310 | -- will override any global settings. The script is defined as a Lua
311 | -- pattern, so that they'll apply to any script whose name matches
312 | -- the pattern.
313 | -- -------------------------------------------------------------------
314 |
315 | instance =
316 | {
317 | ['^/private/raw.*'] =
318 | {
319 | cwd = '/var/tmp',
320 |
321 | -- -----------------------------------------------------------------
322 | -- If you need to specify arguments to the script, define them here.
323 | -- -----------------------------------------------------------------
324 |
325 | arg =
326 | {
327 | "first-argument",
328 | "second-argument",
329 | "third-argument",
330 | },
331 |
332 | -- ---------------------------------------------
333 | -- Additional environment variables per script.
334 | -- ---------------------------------------------
335 |
336 | env =
337 | {
338 | SAMPLE_CONFIG = "sample.config",
339 | PATH = "/var/bin",
340 | },
341 | },
342 | },
343 | }
344 |
--------------------------------------------------------------------------------
/share/about.txt:
--------------------------------------------------------------------------------
1 |
2 | This is a test file about the server. It should also include information
3 | about the person or oganization who runs the server.
4 |
5 |
--------------------------------------------------------------------------------
/share/index.gopher:
--------------------------------------------------------------------------------
1 | iWelcome to your new Gopher Server! example.com 70
2 | i example.com 70
3 | iSorry, no dynamic output here. example.com 70
4 | i example.com 70
5 | 1Alternative index page example.com 70
6 | 0About the site /about.txt example.com 70
7 | 0News of the Day /motd example.com 70
8 | 1Archive of useful stuff /archive/ example.com 70
9 | 1Some tests are here. /test/ example.com 70
10 | i example.com 70
11 | iLinks to other Gopher resources ... example.com 70
12 | i example.com 70
13 | 1Home of port70 Gopher:Src: gopher.conman.org 70
14 | hGithub Repo URL:https://github.com/spc476 example.com 70
15 | 1Other Gopher Servers /world gopher.floodgap.com 70
16 | 1Phlog aggregator /bongusta/ i-logout.cz 70
17 | 1Another Phlog aggregator /moku-pona gopher.black 70
18 | 1Lobste.rs mirror /users/julienxx/Lobste.rs sdf.org 70
19 | 1phlogs /phlogs/ sdf.org 70
20 | 1some stuff sdf.org 70
21 |
--------------------------------------------------------------------------------
/share/index.port70:
--------------------------------------------------------------------------------
1 | Welcome to your new Gopher Server!
2 |
3 | Lua{
4 | local string = require "string"
5 | local os = require "os"
6 | local mklink = require "port70.mklink"
7 | return mklink {
8 | type = 'info',
9 | display = string.format("Today is %s",os.date("%c")),
10 | }
11 | }Lua
12 |
13 | dir /index.gopher Alternative index page
14 | file /about.txt About the site
15 | file /motd News of the Day
16 | dir /archive/ Archive of useful stuff
17 | dir /test/ Some tests are here.
18 |
19 | Links to other Gopher resources ...
20 |
21 | url gopher://gopher.conman.org/1Gopher:Src: Home of port70
22 | url https://github.com/spc476 Github Repo
23 | url gopher://gopher.floodgap.com/1/world Other Gopher Servers
24 | url gopher://i-logout.cz/1/bongusta/ Phlog aggregator
25 | url gopher://gopher.black/1/moku-pona Another Phlog aggregator
26 | url gopher://sdf.org/1/users/julienxx/Lobste.rs Lobste.rs mirror
27 | url gopher://sdf.org/1/phlogs/ phlogs
28 | url gopher://sdf.org/ some stuff
29 |
--------------------------------------------------------------------------------
/share/test/hello.txt:
--------------------------------------------------------------------------------
1 | Hello, world.
2 |
--------------------------------------------------------------------------------