|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)cmds.c 4.9 (Berkeley) 7/26/83";
3: #endif
4:
5: /*
6: * FTP User Program -- Command Routines.
7: */
8: #include <sys/param.h>
9: #include <sys/stat.h>
10: #include <sys/socket.h>
11:
12: #include <arpa/ftp.h>
13:
14: #include <signal.h>
15: #include <stdio.h>
16: #include <errno.h>
17: #include <netdb.h>
18:
19: #include "ftp_var.h"
20:
21: extern char *globerr;
22: extern char **glob();
23: extern char *home;
24: extern short gflag;
25: extern char *remglob();
26: extern char *getenv();
27: extern char *index();
28: extern char *rindex();
29:
30: /*
31: * Connect to peer server and
32: * auto-login, if possible.
33: */
34: setpeer(argc, argv)
35: int argc;
36: char *argv[];
37: {
38: struct hostent *host, *hookup();
39: int port;
40:
41: if (connected) {
42: printf("Already connected to %s, use disconnect first.\n",
43: hostname);
44: return;
45: }
46: if (argc < 2) {
47: strcat(line, " ");
48: printf("(to) ");
49: gets(&line[strlen(line)]);
50: makeargv();
51: argc = margc;
52: argv = margv;
53: }
54: if (argc > 3) {
55: printf("usage: %s host-name [port]\n", argv[0]);
56: return;
57: }
58: port = sp->s_port;
59: if (argc > 2) {
60: port = atoi(argv[2]);
61: if (port <= 0) {
62: printf("%s: bad port number-- %s\n", argv[1], argv[2]);
63: printf ("usage: %s host-name [port]\n", argv[0]);
64: return;
65: }
66: port = htons(port);
67: }
68: host = hookup(argv[1], port);
69: if (host) {
70: connected = 1;
71: if (autologin)
72: login(host);
73: }
74: }
75:
76: struct types {
77: char *t_name;
78: char *t_mode;
79: int t_type;
80: char *t_arg;
81: } types[] = {
82: { "ascii", "A", TYPE_A, 0 },
83: { "binary", "I", TYPE_I, 0 },
84: { "image", "I", TYPE_I, 0 },
85: { "ebcdic", "E", TYPE_E, 0 },
86: { "tenex", "L", TYPE_L, bytename },
87: 0
88: };
89:
90: /*
91: * Set transfer type.
92: */
93: settype(argc, argv)
94: char *argv[];
95: {
96: register struct types *p;
97: int comret;
98:
99: if (argc > 2) {
100: char *sep;
101:
102: printf("usage: %s [", argv[0]);
103: sep = " ";
104: for (p = types; p->t_name; p++) {
105: printf("%s%s", sep, p->t_name);
106: if (*sep == ' ')
107: sep = " | ";
108: }
109: printf(" ]\n");
110: return;
111: }
112: if (argc < 2) {
113: printf("Using %s mode to transfer files.\n", typename);
114: return;
115: }
116: for (p = types; p->t_name; p++)
117: if (strcmp(argv[1], p->t_name) == 0)
118: break;
119: if (p->t_name == 0) {
120: printf("%s: unknown mode\n", argv[1]);
121: return;
122: }
123: if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
124: comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
125: else
126: comret = command("TYPE %s", p->t_mode);
127: if (comret == COMPLETE) {
128: strcpy(typename, p->t_name);
129: type = p->t_type;
130: }
131: }
132:
133: /*
134: * Set binary transfer type.
135: */
136: /*VARARGS*/
137: setbinary()
138: {
139:
140: call(settype, "type", "binary", 0);
141: }
142:
143: /*
144: * Set ascii transfer type.
145: */
146: /*VARARGS*/
147: setascii()
148: {
149:
150: call(settype, "type", "ascii", 0);
151: }
152:
153: /*
154: * Set tenex transfer type.
155: */
156: /*VARARGS*/
157: settenex()
158: {
159:
160: call(settype, "type", "tenex", 0);
161: }
162:
163: /*
164: * Set ebcdic transfer type.
165: */
166: /*VARARGS*/
167: setebcdic()
168: {
169:
170: call(settype, "type", "ebcdic", 0);
171: }
172:
173: /*
174: * Set file transfer mode.
175: */
176: setmode(argc, argv)
177: char *argv[];
178: {
179:
180: printf("We only support %s mode, sorry.\n", modename);
181: }
182:
183: /*
184: * Set file transfer format.
185: */
186: setform(argc, argv)
187: char *argv[];
188: {
189:
190: printf("We only support %s format, sorry.\n", formname);
191: }
192:
193: /*
194: * Set file transfer structure.
195: */
196: setstruct(argc, argv)
197: char *argv[];
198: {
199:
200: printf("We only support %s structure, sorry.\n", structname);
201: }
202:
203: put(argc, argv)
204: int argc;
205: char *argv[];
206: {
207: char *cmd;
208:
209: if (argc == 2)
210: argc++, argv[2] = argv[1];
211: if (argc < 2) {
212: strcat(line, " ");
213: printf("(local-file) ");
214: gets(&line[strlen(line)]);
215: makeargv();
216: argc = margc;
217: argv = margv;
218: }
219: if (argc < 2) {
220: usage:
221: printf("%s local-file remote-file\n", argv[0]);
222: return;
223: }
224: if (argc < 3) {
225: strcat(line, " ");
226: printf("(remote-file) ");
227: gets(&line[strlen(line)]);
228: makeargv();
229: argc = margc;
230: argv = margv;
231: }
232: if (argc < 3)
233: goto usage;
234: if (!globulize(&argv[1]))
235: return;
236: cmd = (argv[0][0] == 'a') ? "APPE" : "STOR";
237: sendrequest(cmd, argv[1], argv[2]);
238: }
239:
240: /*
241: * Send multiple files.
242: */
243: mput(argc, argv)
244: char *argv[];
245: {
246: register int i;
247:
248: if (argc < 2) {
249: strcat(line, " ");
250: printf("(local-files) ");
251: gets(&line[strlen(line)]);
252: makeargv();
253: argc = margc;
254: argv = margv;
255: }
256: if (argc < 2) {
257: printf("%s local-files\n", argv[0]);
258: return;
259: }
260: for (i = 1; i < argc; i++) {
261: register char **cpp, **gargs;
262:
263: if (!doglob) {
264: if (confirm(argv[0], argv[i]))
265: sendrequest("STOR", argv[i], argv[i]);
266: continue;
267: }
268: gargs = glob(argv[i]);
269: if (globerr != NULL) {
270: printf("%s\n", globerr);
271: if (gargs)
272: blkfree(gargs);
273: continue;
274: }
275: for (cpp = gargs; cpp && *cpp != NULL; cpp++)
276: if (confirm(argv[0], *cpp))
277: sendrequest("STOR", *cpp, *cpp);
278: if (gargs != NULL)
279: blkfree(gargs);
280: }
281: }
282:
283: /*
284: * Receive one file.
285: */
286: get(argc, argv)
287: char *argv[];
288: {
289:
290: if (argc == 2)
291: argc++, argv[2] = argv[1];
292: if (argc < 2) {
293: strcat(line, " ");
294: printf("(remote-file) ");
295: gets(&line[strlen(line)]);
296: makeargv();
297: argc = margc;
298: argv = margv;
299: }
300: if (argc < 2) {
301: usage:
302: printf("%s remote-file [ local-file ]\n", argv[0]);
303: return;
304: }
305: if (argc < 3) {
306: strcat(line, " ");
307: printf("(local-file) ");
308: gets(&line[strlen(line)]);
309: makeargv();
310: argc = margc;
311: argv = margv;
312: }
313: if (argc < 3)
314: goto usage;
315: if (!globulize(&argv[2]))
316: return;
317: recvrequest("RETR", argv[2], argv[1], "w");
318: }
319:
320: /*
321: * Get multiple files.
322: */
323: mget(argc, argv)
324: char *argv[];
325: {
326: char *cp;
327:
328: if (argc < 2) {
329: strcat(line, " ");
330: printf("(remote-files) ");
331: gets(&line[strlen(line)]);
332: makeargv();
333: argc = margc;
334: argv = margv;
335: }
336: if (argc < 2) {
337: printf("%s remote-files\n", argv[0]);
338: return;
339: }
340: while ((cp = remglob(argc, argv)) != NULL)
341: if (confirm(argv[0], cp))
342: recvrequest("RETR", cp, cp, "w");
343: }
344:
345: char *
346: remglob(argc, argv)
347: char *argv[];
348: {
349: char temp[16];
350: static char buf[MAXPATHLEN];
351: static FILE *ftemp = NULL;
352: static char **args;
353: int oldverbose, oldhash;
354: char *cp, *mode;
355:
356: if (!doglob) {
357: if (args == NULL)
358: args = argv;
359: if ((cp = *++args) == NULL)
360: args = NULL;
361: return (cp);
362: }
363: if (ftemp == NULL) {
364: strcpy(temp, "/tmp/ftpXXXXXX");
365: mktemp(temp);
366: oldverbose = verbose, verbose = 0;
367: oldhash = hash, hash = 0;
368: for (mode = "w"; *++argv != NULL; mode = "a")
369: recvrequest ("NLST", temp, *argv, mode);
370: verbose = oldverbose; hash = oldhash;
371: ftemp = fopen(temp, "r");
372: unlink(temp);
373: if (ftemp == NULL) {
374: printf("can't find list of remote files, oops\n");
375: return (NULL);
376: }
377: }
378: if (fgets(buf, sizeof (buf), ftemp) == NULL) {
379: fclose(ftemp), ftemp = NULL;
380: return (NULL);
381: }
382: if ((cp = index(buf, '\n')) != NULL)
383: *cp = '\0';
384: return (buf);
385: }
386:
387: char *
388: onoff(bool)
389: int bool;
390: {
391:
392: return (bool ? "on" : "off");
393: }
394:
395: /*
396: * Show status.
397: */
398: status(argc, argv)
399: char *argv[];
400: {
401:
402: if (connected)
403: printf("Connected to %s.\n", hostname);
404: else
405: printf("Not connected.\n");
406: printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
407: modename, typename, formname, structname);
408: printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
409: onoff(verbose), onoff(bell), onoff(interactive),
410: onoff(doglob));
411: printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
412: onoff(hash), onoff(sendport));
413: }
414:
415: /*
416: * Set beep on cmd completed mode.
417: */
418: /*VARARGS*/
419: setbell()
420: {
421:
422: bell = !bell;
423: printf("Bell mode %s.\n", onoff(bell));
424: }
425:
426: /*
427: * Turn on packet tracing.
428: */
429: /*VARARGS*/
430: settrace()
431: {
432:
433: trace = !trace;
434: printf("Packet tracing %s.\n", onoff(trace));
435: }
436:
437: /*
438: * Toggle hash mark printing during transfers.
439: */
440: /*VARARGS*/
441: sethash()
442: {
443:
444: hash = !hash;
445: printf("Hash mark printing %s", onoff(hash));
446: if (hash)
447: printf(" (%d bytes/hash mark)", BUFSIZ);
448: printf(".\n");
449: }
450:
451: /*
452: * Turn on printing of server echo's.
453: */
454: /*VARARGS*/
455: setverbose()
456: {
457:
458: verbose = !verbose;
459: printf("Verbose mode %s.\n", onoff(verbose));
460: }
461:
462: /*
463: * Toggle PORT cmd use before each data connection.
464: */
465: /*VARARGS*/
466: setport()
467: {
468:
469: sendport = !sendport;
470: printf("Use of PORT cmds %s.\n", onoff(sendport));
471: }
472:
473: /*
474: * Turn on interactive prompting
475: * during mget, mput, and mdelete.
476: */
477: /*VARARGS*/
478: setprompt()
479: {
480:
481: interactive = !interactive;
482: printf("Interactive mode %s.\n", onoff(interactive));
483: }
484:
485: /*
486: * Toggle metacharacter interpretation
487: * on local file names.
488: */
489: /*VARARGS*/
490: setglob()
491: {
492:
493: doglob = !doglob;
494: printf("Globbing %s.\n", onoff(doglob));
495: }
496:
497: /*
498: * Set debugging mode on/off and/or
499: * set level of debugging.
500: */
501: /*VARARGS*/
502: setdebug(argc, argv)
503: char *argv[];
504: {
505: int val;
506:
507: if (argc > 1) {
508: val = atoi(argv[1]);
509: if (val < 0) {
510: printf("%s: bad debugging value.\n", argv[1]);
511: return;
512: }
513: } else
514: val = !debug;
515: debug = val;
516: if (debug)
517: options |= SO_DEBUG;
518: else
519: options &= ~SO_DEBUG;
520: printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
521: }
522:
523: /*
524: * Set current working directory
525: * on remote machine.
526: */
527: cd(argc, argv)
528: char *argv[];
529: {
530:
531: if (argc < 2) {
532: strcat(line, " ");
533: printf("(remote-directory) ");
534: gets(&line[strlen(line)]);
535: makeargv();
536: argc = margc;
537: argv = margv;
538: }
539: if (argc < 2) {
540: printf("%s remote-directory\n", argv[0]);
541: return;
542: }
543: (void) command("CWD %s", argv[1]);
544: }
545:
546: /*
547: * Set current working directory
548: * on local machine.
549: */
550: lcd(argc, argv)
551: char *argv[];
552: {
553: char buf[MAXPATHLEN];
554:
555: if (argc < 2)
556: argc++, argv[1] = home;
557: if (argc != 2) {
558: printf("%s local-directory\n", argv[0]);
559: return;
560: }
561: if (!globulize(&argv[1]))
562: return;
563: if (chdir(argv[1]) < 0) {
564: perror(argv[1]);
565: return;
566: }
567: printf("Local directory now %s\n", getwd(buf));
568: }
569:
570: /*
571: * Delete a single file.
572: */
573: delete(argc, argv)
574: char *argv[];
575: {
576:
577: if (argc < 2) {
578: strcat(line, " ");
579: printf("(remote-file) ");
580: gets(&line[strlen(line)]);
581: makeargv();
582: argc = margc;
583: argv = margv;
584: }
585: if (argc < 2) {
586: printf("%s remote-file\n", argv[0]);
587: return;
588: }
589: (void) command("DELE %s", argv[1]);
590: }
591:
592: /*
593: * Delete multiple files.
594: */
595: mdelete(argc, argv)
596: char *argv[];
597: {
598: char *cp;
599:
600: if (argc < 2) {
601: strcat(line, " ");
602: printf("(remote-files) ");
603: gets(&line[strlen(line)]);
604: makeargv();
605: argc = margc;
606: argv = margv;
607: }
608: if (argc < 2) {
609: printf("%s remote-files\n", argv[0]);
610: return;
611: }
612: while ((cp = remglob(argc, argv)) != NULL)
613: if (confirm(argv[0], cp))
614: (void) command("DELE %s", cp);
615: }
616:
617: /*
618: * Rename a remote file.
619: */
620: renamefile(argc, argv)
621: char *argv[];
622: {
623:
624: if (argc < 2) {
625: strcat(line, " ");
626: printf("(from-name) ");
627: gets(&line[strlen(line)]);
628: makeargv();
629: argc = margc;
630: argv = margv;
631: }
632: if (argc < 2) {
633: usage:
634: printf("%s from-name to-name\n", argv[0]);
635: return;
636: }
637: if (argc < 3) {
638: strcat(line, " ");
639: printf("(to-name) ");
640: gets(&line[strlen(line)]);
641: makeargv();
642: argc = margc;
643: argv = margv;
644: }
645: if (argc < 3)
646: goto usage;
647: if (command("RNFR %s", argv[1]) == CONTINUE)
648: (void) command("RNTO %s", argv[2]);
649: }
650:
651: /*
652: * Get a directory listing
653: * of remote files.
654: */
655: ls(argc, argv)
656: char *argv[];
657: {
658: char *cmd;
659:
660: if (argc < 2)
661: argc++, argv[1] = NULL;
662: if (argc < 3)
663: argc++, argv[2] = "-";
664: if (argc > 3) {
665: printf("usage: %s remote-directory local-file\n", argv[0]);
666: return;
667: }
668: cmd = argv[0][0] == 'l' ? "NLST" : "LIST";
669: if (strcmp(argv[2], "-") && !globulize(&argv[2]))
670: return;
671: recvrequest(cmd, argv[2], argv[1], "w");
672: }
673:
674: /*
675: * Get a directory listing
676: * of multiple remote files.
677: */
678: mls(argc, argv)
679: char *argv[];
680: {
681: char *cmd, *mode, *cp, *dest;
682:
683: if (argc < 2) {
684: strcat(line, " ");
685: printf("(remote-files) ");
686: gets(&line[strlen(line)]);
687: makeargv();
688: argc = margc;
689: argv = margv;
690: }
691: if (argc < 3) {
692: strcat(line, " ");
693: printf("(local-file) ");
694: gets(&line[strlen(line)]);
695: makeargv();
696: argc = margc;
697: argv = margv;
698: }
699: if (argc < 3) {
700: printf("%s remote-files local-file\n", argv[0]);
701: return;
702: }
703: dest = argv[argc - 1];
704: argv[argc - 1] = NULL;
705: if (strcmp(dest, "-"))
706: if (globulize(&dest) && confirm("local-file", dest))
707: return;
708: cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
709: for (mode = "w"; cp = remglob(argc, argv); mode = "a")
710: if (confirm(argv[0], cp))
711: recvrequest(cmd, dest, cp, mode);
712: }
713:
714: /*
715: * Do a shell escape
716: */
717: shell(argc, argv)
718: char *argv[];
719: {
720: int pid, status, (*old1)(), (*old2)();
721: char shellnam[40], *shell, *namep;
722: char **cpp, **gargs;
723:
724: old1 = signal (SIGINT, SIG_IGN);
725: old2 = signal (SIGQUIT, SIG_IGN);
726: if ((pid = fork()) == 0) {
727: for (pid = 3; pid < 20; pid++)
728: close(pid);
729: signal(SIGINT, SIG_DFL);
730: signal(SIGQUIT, SIG_DFL);
731: if (argc <= 1) {
732: shell = getenv("SHELL");
733: if (shell == NULL)
734: shell = "/bin/sh";
735: namep = rindex(shell,'/');
736: if (namep == NULL)
737: namep = shell;
738: strcpy(shellnam,"-");
739: strcat(shellnam, ++namep);
740: if (strcmp(namep, "sh") != 0)
741: shellnam[0] = '+';
742: if (debug) {
743: printf ("%s\n", shell);
744: fflush (stdout);
745: }
746: execl(shell, shellnam, 0);
747: perror(shell);
748: exit(1);
749: }
750: cpp = &argv[1];
751: if (argc > 2) {
752: if ((gargs = glob(cpp)) != NULL)
753: cpp = gargs;
754: if (globerr != NULL) {
755: printf("%s\n", globerr);
756: exit(1);
757: }
758: }
759: if (debug) {
760: register char **zip = cpp;
761:
762: printf("%s", *zip);
763: while (*++zip != NULL)
764: printf(" %s", *zip);
765: printf("\n");
766: fflush(stdout);
767: }
768: execvp(argv[1], cpp);
769: perror(argv[1]);
770: exit(1);
771: }
772: if (pid > 0)
773: while (wait(&status) != pid)
774: ;
775: signal(SIGINT, old1);
776: signal(SIGQUIT, old2);
777: if (pid == -1)
778: perror("Try again later");
779: return (0);
780: }
781:
782: /*
783: * Send new user information (re-login)
784: */
785: user(argc, argv)
786: int argc;
787: char **argv;
788: {
789: char acct[80], *getpass();
790: int n;
791:
792: if (argc < 2) {
793: strcat(line, " ");
794: printf("(username) ");
795: gets(&line[strlen(line)]);
796: makeargv();
797: argc = margc;
798: argv = margv;
799: }
800: if (argc > 4) {
801: printf("usage: %s username [password] [account]\n", argv[0]);
802: return (0);
803: }
804: n = command("USER %s", argv[1]);
805: if (n == CONTINUE) {
806: if (argc < 3 )
807: argv[2] = getpass("Password: "), argc++;
808: n = command("PASS %s", argv[2]);
809: }
810: if (n == CONTINUE) {
811: if (argc < 4) {
812: printf("Account: "); (void) fflush(stdout);
813: (void) fgets(acct, sizeof(acct) - 1, stdin);
814: acct[strlen(acct) - 1] = '\0';
815: argv[3] = acct; argc++;
816: }
817: n = command("ACCT %s", acct);
818: }
819: if (n != COMPLETE) {
820: fprintf(stderr, "Login failed.\n");
821: return (0);
822: }
823: return (1);
824: }
825:
826: /*
827: * Print working directory.
828: */
829: /*VARARGS*/
830: pwd()
831: {
832:
833: (void) command("XPWD");
834: }
835:
836: /*
837: * Make a directory.
838: */
839: makedir(argc, argv)
840: char *argv[];
841: {
842:
843: if (argc < 2) {
844: strcat(line, " ");
845: printf("(directory-name) ");
846: gets(&line[strlen(line)]);
847: makeargv();
848: argc = margc;
849: argv = margv;
850: }
851: if (argc < 2) {
852: printf("%s directory-name\n", argv[0]);
853: return;
854: }
855: (void) command("XMKD %s", argv[1]);
856: }
857:
858: /*
859: * Remove a directory.
860: */
861: removedir(argc, argv)
862: char *argv[];
863: {
864:
865: if (argc < 2) {
866: strcat(line, " ");
867: printf("(directory-name) ");
868: gets(&line[strlen(line)]);
869: makeargv();
870: argc = margc;
871: argv = margv;
872: }
873: if (argc < 2) {
874: printf("%s directory-name\n", argv[0]);
875: return;
876: }
877: (void) command("XRMD %s", argv[1]);
878: }
879:
880: /*
881: * Send a line, verbatim, to the remote machine.
882: */
883: quote(argc, argv)
884: char *argv[];
885: {
886: int i;
887: char buf[BUFSIZ];
888:
889: if (argc < 2) {
890: strcat(line, " ");
891: printf("(command line to send) ");
892: gets(&line[strlen(line)]);
893: makeargv();
894: argc = margc;
895: argv = margv;
896: }
897: if (argc < 2) {
898: printf("usage: %s line-to-send\n", argv[0]);
899: return;
900: }
901: strcpy(buf, argv[1]);
902: for (i = 2; i < argc; i++) {
903: strcat(buf, " ");
904: strcat(buf, argv[i]);
905: }
906: (void) command(buf);
907: }
908:
909: /*
910: * Ask the other side for help.
911: */
912: rmthelp(argc, argv)
913: char *argv[];
914: {
915: int oldverbose = verbose;
916:
917: verbose = 1;
918: (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
919: verbose = oldverbose;
920: }
921:
922: /*
923: * Terminate session and exit.
924: */
925: /*VARARGS*/
926: quit()
927: {
928:
929: disconnect();
930: exit(0);
931: }
932:
933: /*
934: * Terminate session, but don't exit.
935: */
936: disconnect()
937: {
938: extern FILE *cout;
939: extern int data;
940:
941: if (!connected)
942: return;
943: (void) command("QUIT");
944: (void) fclose(cout);
945: cout = NULL;
946: connected = 0;
947: data = -1;
948: }
949:
950: confirm(cmd, file)
951: char *cmd, *file;
952: {
953: char line[BUFSIZ];
954:
955: if (!interactive)
956: return (1);
957: printf("%s %s? ", cmd, file);
958: fflush(stdout);
959: gets(line);
960: return (*line != 'n' && *line != 'N');
961: }
962:
963: fatal(msg)
964: char *msg;
965: {
966:
967: fprintf(stderr, "ftp: %s\n");
968: exit(1);
969: }
970:
971: /*
972: * Glob a local file name specification with
973: * the expectation of a single return value.
974: * Can't control multiple values being expanded
975: * from the expression, we return only the first.
976: */
977: globulize(cpp)
978: char **cpp;
979: {
980: char **globbed;
981:
982: if (!doglob)
983: return (1);
984: globbed = glob(*cpp);
985: if (globerr != NULL) {
986: printf("%s: %s\n", *cpp, globerr);
987: if (globbed)
988: blkfree(globbed);
989: return (0);
990: }
991: if (globbed) {
992: *cpp = *globbed++;
993: /* don't waste too much memory */
994: if (*globbed)
995: blkfree(globbed);
996: }
997: return (1);
998: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.