Merge branch 'vendor/OPENSSH'
[dragonfly.git] / contrib / tnftp / util.c
1 /*      $NetBSD: util.c,v 1.155 2010/06/05 13:59:39 lukem Exp $ */
2
3 /*-
4  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Luke Mewburn.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12  * NASA Ames Research Center.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 /*
37  * Copyright (c) 1985, 1989, 1993, 1994
38  *      The Regents of the University of California.  All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  */
64
65 #include <sys/cdefs.h>
66 #ifndef lint
67 __RCSID("$NetBSD: util.c,v 1.155 2010/06/05 13:59:39 lukem Exp $");
68 #endif /* not lint */
69
70 /*
71  * FTP User Program -- Misc support routines
72  */
73 #include <sys/param.h>
74 #include <sys/socket.h>
75 #include <sys/ioctl.h>
76 #include <sys/time.h>
77 #include <netinet/in.h>
78 #include <arpa/ftp.h>
79
80 #include <ctype.h>
81 #include <err.h>
82 #include <errno.h>
83 #include <fcntl.h>
84 #include <glob.h>
85 #include <signal.h>
86 #include <libgen.h>
87 #include <limits.h>
88 #include <locale.h>
89 #include <netdb.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <termios.h>
94 #include <time.h>
95 #include <tzfile.h>
96 #include <unistd.h>
97
98 #include "ftp_var.h"
99
100 /*
101  * Connect to peer server and auto-login, if possible.
102  */
103 void
104 setpeer(int argc, char *argv[])
105 {
106         char *host;
107         const char *port;
108
109         if (argc == 0)
110                 goto usage;
111         if (connected) {
112                 fprintf(ttyout, "Already connected to %s, use close first.\n",
113                     hostname);
114                 code = -1;
115                 return;
116         }
117         if (argc < 2)
118                 (void)another(&argc, &argv, "to");
119         if (argc < 2 || argc > 3) {
120  usage:
121                 UPRINTF("usage: %s host-name [port]\n", argv[0]);
122                 code = -1;
123                 return;
124         }
125         if (gatemode)
126                 port = gateport;
127         else
128                 port = ftpport;
129         if (argc > 2)
130                 port = argv[2];
131
132         if (gatemode) {
133                 if (gateserver == NULL || *gateserver == '\0')
134                         errx(1, "main: gateserver not defined");
135                 host = hookup(gateserver, port);
136         } else
137                 host = hookup(argv[1], port);
138
139         if (host) {
140                 if (gatemode && verbose) {
141                         fprintf(ttyout,
142                             "Connecting via pass-through server %s\n",
143                             gateserver);
144                 }
145
146                 connected = 1;
147                 /*
148                  * Set up defaults for FTP.
149                  */
150                 (void)strlcpy(typename, "ascii", sizeof(typename));
151                 type = TYPE_A;
152                 curtype = TYPE_A;
153                 (void)strlcpy(formname, "non-print", sizeof(formname));
154                 form = FORM_N;
155                 (void)strlcpy(modename, "stream", sizeof(modename));
156                 mode = MODE_S;
157                 (void)strlcpy(structname, "file", sizeof(structname));
158                 stru = STRU_F;
159                 (void)strlcpy(bytename, "8", sizeof(bytename));
160                 bytesize = 8;
161                 if (autologin)
162                         (void)ftp_login(argv[1], NULL, NULL);
163         }
164 }
165
166 static void
167 parse_feat(const char *fline)
168 {
169
170                         /*
171                          * work-around broken ProFTPd servers that can't
172                          * even obey RFC 2389.
173                          */
174         while (*fline && isspace((int)*fline))
175                 fline++;
176
177         if (strcasecmp(fline, "MDTM") == 0)
178                 features[FEAT_MDTM] = 1;
179         else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) {
180                 features[FEAT_MLST] = 1;
181         } else if (strcasecmp(fline, "REST STREAM") == 0)
182                 features[FEAT_REST_STREAM] = 1;
183         else if (strcasecmp(fline, "SIZE") == 0)
184                 features[FEAT_SIZE] = 1;
185         else if (strcasecmp(fline, "TVFS") == 0)
186                 features[FEAT_TVFS] = 1;
187 }
188
189 /*
190  * Determine the remote system type (SYST) and features (FEAT).
191  * Call after a successful login (i.e, connected = -1)
192  */
193 void
194 getremoteinfo(void)
195 {
196         int overbose, i;
197
198         overbose = verbose;
199         if (ftp_debug == 0)
200                 verbose = -1;
201
202                         /* determine remote system type */
203         if (command("SYST") == COMPLETE) {
204                 if (overbose) {
205                         char *cp, c;
206
207                         c = 0;
208                         cp = strchr(reply_string + 4, ' ');
209                         if (cp == NULL)
210                                 cp = strchr(reply_string + 4, '\r');
211                         if (cp) {
212                                 if (cp[-1] == '.')
213                                         cp--;
214                                 c = *cp;
215                                 *cp = '\0';
216                         }
217
218                         fprintf(ttyout, "Remote system type is %s.\n",
219                             reply_string + 4);
220                         if (cp)
221                                 *cp = c;
222                 }
223                 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
224                         if (proxy)
225                                 unix_proxy = 1;
226                         else
227                                 unix_server = 1;
228                         /*
229                          * Set type to 0 (not specified by user),
230                          * meaning binary by default, but don't bother
231                          * telling server.  We can use binary
232                          * for text files unless changed by the user.
233                          */
234                         type = 0;
235                         (void)strlcpy(typename, "binary", sizeof(typename));
236                         if (overbose)
237                             fprintf(ttyout,
238                                 "Using %s mode to transfer files.\n",
239                                 typename);
240                 } else {
241                         if (proxy)
242                                 unix_proxy = 0;
243                         else
244                                 unix_server = 0;
245                         if (overbose &&
246                             !strncmp(reply_string, "215 TOPS20", 10))
247                                 fputs(
248 "Remember to set tenex mode when transferring binary files from this machine.\n",
249                                     ttyout);
250                 }
251         }
252
253                         /* determine features (if any) */
254         for (i = 0; i < FEAT_max; i++)
255                 features[i] = -1;
256         reply_callback = parse_feat;
257         if (command("FEAT") == COMPLETE) {
258                 for (i = 0; i < FEAT_max; i++) {
259                         if (features[i] == -1)
260                                 features[i] = 0;
261                 }
262                 features[FEAT_FEAT] = 1;
263         } else
264                 features[FEAT_FEAT] = 0;
265 #ifndef NO_DEBUG
266         if (ftp_debug) {
267 #define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)])
268                 DEBUG_FEAT(FEAT_FEAT);
269                 DEBUG_FEAT(FEAT_MDTM);
270                 DEBUG_FEAT(FEAT_MLST);
271                 DEBUG_FEAT(FEAT_REST_STREAM);
272                 DEBUG_FEAT(FEAT_SIZE);
273                 DEBUG_FEAT(FEAT_TVFS);
274 #undef DEBUG_FEAT
275         }
276 #endif
277         reply_callback = NULL;
278
279         verbose = overbose;
280 }
281
282 /*
283  * Reset the various variables that indicate connection state back to
284  * disconnected settings.
285  * The caller is responsible for issuing any commands to the remote server
286  * to perform a clean shutdown before this is invoked.
287  */
288 void
289 cleanuppeer(void)
290 {
291
292         if (cout)
293                 (void)fclose(cout);
294         cout = NULL;
295         connected = 0;
296         unix_server = 0;
297         unix_proxy = 0;
298                         /*
299                          * determine if anonftp was specifically set with -a
300                          * (1), or implicitly set by auto_fetch() (2). in the
301                          * latter case, disable after the current xfer
302                          */
303         if (anonftp == 2)
304                 anonftp = 0;
305         data = -1;
306         epsv4bad = 0;
307         epsv6bad = 0;
308         if (username)
309                 free(username);
310         username = NULL;
311         if (!proxy)
312                 macnum = 0;
313 }
314
315 /*
316  * Top-level signal handler for interrupted commands.
317  */
318 void
319 intr(int signo)
320 {
321
322         sigint_raised = 1;
323         alarmtimer(0);
324         if (fromatty)
325                 write(fileno(ttyout), "\n", 1);
326         siglongjmp(toplevel, 1);
327 }
328
329 /*
330  * Signal handler for lost connections; cleanup various elements of
331  * the connection state, and call cleanuppeer() to finish it off.
332  */
333 void
334 lostpeer(int dummy)
335 {
336         int oerrno = errno;
337
338         alarmtimer(0);
339         if (connected) {
340                 if (cout != NULL) {
341                         (void)shutdown(fileno(cout), 1+1);
342                         (void)fclose(cout);
343                         cout = NULL;
344                 }
345                 if (data >= 0) {
346                         (void)shutdown(data, 1+1);
347                         (void)close(data);
348                         data = -1;
349                 }
350                 connected = 0;
351         }
352         pswitch(1);
353         if (connected) {
354                 if (cout != NULL) {
355                         (void)shutdown(fileno(cout), 1+1);
356                         (void)fclose(cout);
357                         cout = NULL;
358                 }
359                 connected = 0;
360         }
361         proxflag = 0;
362         pswitch(0);
363         cleanuppeer();
364         errno = oerrno;
365 }
366
367
368 /*
369  * Login to remote host, using given username & password if supplied.
370  * Return non-zero if successful.
371  */
372 int
373 ftp_login(const char *host, const char *luser, const char *lpass)
374 {
375         char tmp[80];
376         char *fuser, *pass, *facct, *p;
377         char emptypass[] = "";
378         const char *errormsg;
379         int n, aflag, rval, nlen;
380
381         aflag = rval = 0;
382         fuser = pass = facct = NULL;
383         if (luser)
384                 fuser = ftp_strdup(luser);
385         if (lpass)
386                 pass = ftp_strdup(lpass);
387
388         DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n",
389             STRorNULL(fuser), STRorNULL(pass), STRorNULL(host));
390
391         /*
392          * Set up arguments for an anonymous FTP session, if necessary.
393          */
394         if (anonftp) {
395                 FREEPTR(fuser);
396                 fuser = ftp_strdup("anonymous");        /* as per RFC 1635 */
397                 FREEPTR(pass);
398                 pass = ftp_strdup(getoptionvalue("anonpass"));
399         }
400
401         if (ruserpass(host, &fuser, &pass, &facct) < 0) {
402                 code = -1;
403                 goto cleanup_ftp_login;
404         }
405
406         while (fuser == NULL) {
407                 if (localname)
408                         fprintf(ttyout, "Name (%s:%s): ", host, localname);
409                 else
410                         fprintf(ttyout, "Name (%s): ", host);
411                 errormsg = NULL;
412                 nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg);
413                 if (nlen < 0) {
414                         fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login");
415                         code = -1;
416                         goto cleanup_ftp_login;
417                 } else if (nlen == 0) {
418                         fuser = ftp_strdup(localname);
419                 } else {
420                         fuser = ftp_strdup(tmp);
421                 }
422         }
423
424         if (gatemode) {
425                 char *nuser;
426                 size_t len;
427
428                 len = strlen(fuser) + 1 + strlen(host) + 1;
429                 nuser = ftp_malloc(len);
430                 (void)strlcpy(nuser, fuser, len);
431                 (void)strlcat(nuser, "@",  len);
432                 (void)strlcat(nuser, host, len);
433                 FREEPTR(fuser);
434                 fuser = nuser;
435         }
436
437         n = command("USER %s", fuser);
438         if (n == CONTINUE) {
439                 if (pass == NULL) {
440                         p = getpass("Password: ");
441                         if (p == NULL)
442                                 p = emptypass;
443                         pass = ftp_strdup(p);
444                         memset(p, 0, strlen(p));
445                 }
446                 n = command("PASS %s", pass);
447                 memset(pass, 0, strlen(pass));
448         }
449         if (n == CONTINUE) {
450                 aflag++;
451                 if (facct == NULL) {
452                         p = getpass("Account: ");
453                         if (p == NULL)
454                                 p = emptypass;
455                         facct = ftp_strdup(p);
456                         memset(p, 0, strlen(p));
457                 }
458                 if (facct[0] == '\0') {
459                         warnx("Login failed");
460                         goto cleanup_ftp_login;
461                 }
462                 n = command("ACCT %s", facct);
463                 memset(facct, 0, strlen(facct));
464         }
465         if ((n != COMPLETE) ||
466             (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) {
467                 warnx("Login failed");
468                 goto cleanup_ftp_login;
469         }
470         rval = 1;
471         username = ftp_strdup(fuser);
472         if (proxy)
473                 goto cleanup_ftp_login;
474
475         connected = -1;
476         getremoteinfo();
477         for (n = 0; n < macnum; ++n) {
478                 if (!strcmp("init", macros[n].mac_name)) {
479                         (void)strlcpy(line, "$init", sizeof(line));
480                         makeargv();
481                         domacro(margc, margv);
482                         break;
483                 }
484         }
485         updatelocalcwd();
486         updateremotecwd();
487
488  cleanup_ftp_login:
489         FREEPTR(fuser);
490         if (pass != NULL)
491                 memset(pass, 0, strlen(pass));
492         FREEPTR(pass);
493         if (facct != NULL)
494                 memset(facct, 0, strlen(facct));
495         FREEPTR(facct);
496         return (rval);
497 }
498
499 /*
500  * `another' gets another argument, and stores the new argc and argv.
501  * It reverts to the top level (via intr()) on EOF/error.
502  *
503  * Returns false if no new arguments have been added.
504  */
505 int
506 another(int *pargc, char ***pargv, const char *aprompt)
507 {
508         const char      *errormsg;
509         int             ret, nlen;
510         size_t          len;
511
512         len = strlen(line);
513         if (len >= sizeof(line) - 3) {
514                 fputs("Sorry, arguments too long.\n", ttyout);
515                 intr(0);
516         }
517         fprintf(ttyout, "(%s) ", aprompt);
518         line[len++] = ' ';
519         errormsg = NULL;
520         nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg);
521         if (nlen < 0) {
522                 fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation");
523                 intr(0);
524         }
525         len += nlen;
526         makeargv();
527         ret = margc > *pargc;
528         *pargc = margc;
529         *pargv = margv;
530         return (ret);
531 }
532
533 /*
534  * glob files given in argv[] from the remote server.
535  * if errbuf isn't NULL, store error messages there instead
536  * of writing to the screen.
537  */
538 char *
539 remglob(char *argv[], int doswitch, const char **errbuf)
540 {
541         static char buf[MAXPATHLEN];
542         static FILE *ftemp = NULL;
543         static char **args;
544         char temp[MAXPATHLEN];
545         int oldverbose, oldhash, oldprogress, fd;
546         char *cp;
547         const char *rmode;
548         size_t len;
549
550         if (!mflag || !connected) {
551                 if (!doglob)
552                         args = NULL;
553                 else {
554                         if (ftemp) {
555                                 (void)fclose(ftemp);
556                                 ftemp = NULL;
557                         }
558                 }
559                 return (NULL);
560         }
561         if (!doglob) {
562                 if (args == NULL)
563                         args = argv;
564                 if ((cp = *++args) == NULL)
565                         args = NULL;
566                 return (cp);
567         }
568         if (ftemp == NULL) {
569                 len = strlcpy(temp, tmpdir, sizeof(temp));
570                 if (temp[len - 1] != '/')
571                         (void)strlcat(temp, "/", sizeof(temp));
572                 (void)strlcat(temp, TMPFILE, sizeof(temp));
573                 if ((fd = mkstemp(temp)) < 0) {
574                         warn("Unable to create temporary file `%s'", temp);
575                         return (NULL);
576                 }
577                 close(fd);
578                 oldverbose = verbose;
579                 verbose = (errbuf != NULL) ? -1 : 0;
580                 oldhash = hash;
581                 oldprogress = progress;
582                 hash = 0;
583                 progress = 0;
584                 if (doswitch)
585                         pswitch(!proxy);
586                 for (rmode = "w"; *++argv != NULL; rmode = "a")
587                         recvrequest("NLST", temp, *argv, rmode, 0, 0);
588                 if ((code / 100) != COMPLETE) {
589                         if (errbuf != NULL)
590                                 *errbuf = reply_string;
591                 }
592                 if (doswitch)
593                         pswitch(!proxy);
594                 verbose = oldverbose;
595                 hash = oldhash;
596                 progress = oldprogress;
597                 ftemp = fopen(temp, "r");
598                 (void)unlink(temp);
599                 if (ftemp == NULL) {
600                         if (errbuf == NULL)
601                                 warnx("Can't find list of remote files");
602                         else
603                                 *errbuf =
604                                     "Can't find list of remote files";
605                         return (NULL);
606                 }
607         }
608         if (fgets(buf, sizeof(buf), ftemp) == NULL) {
609                 (void)fclose(ftemp);
610                 ftemp = NULL;
611                 return (NULL);
612         }
613         if ((cp = strchr(buf, '\n')) != NULL)
614                 *cp = '\0';
615         return (buf);
616 }
617
618 /*
619  * Glob a local file name specification with the expectation of a single
620  * return value. Can't control multiple values being expanded from the
621  * expression, we return only the first.
622  * Returns NULL on error, or a pointer to a buffer containing the filename
623  * that's the caller's responsiblity to free(3) when finished with.
624  */
625 char *
626 globulize(const char *pattern)
627 {
628         glob_t gl;
629         int flags;
630         char *p;
631
632         if (!doglob)
633                 return (ftp_strdup(pattern));
634
635         flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
636         memset(&gl, 0, sizeof(gl));
637         if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
638                 warnx("Glob pattern `%s' not found", pattern);
639                 globfree(&gl);
640                 return (NULL);
641         }
642         p = ftp_strdup(gl.gl_pathv[0]);
643         globfree(&gl);
644         return (p);
645 }
646
647 /*
648  * determine size of remote file
649  */
650 off_t
651 remotesize(const char *file, int noisy)
652 {
653         int overbose, r;
654         off_t size;
655
656         overbose = verbose;
657         size = -1;
658         if (ftp_debug == 0)
659                 verbose = -1;
660         if (! features[FEAT_SIZE]) {
661                 if (noisy)
662                         fprintf(ttyout,
663                             "SIZE is not supported by remote server.\n");
664                 goto cleanup_remotesize;
665         }
666         r = command("SIZE %s", file);
667         if (r == COMPLETE) {
668                 char *cp, *ep;
669
670                 cp = strchr(reply_string, ' ');
671                 if (cp != NULL) {
672                         cp++;
673                         size = STRTOLL(cp, &ep, 10);
674                         if (*ep != '\0' && !isspace((unsigned char)*ep))
675                                 size = -1;
676                 }
677         } else {
678                 if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1)
679                         features[FEAT_SIZE] = 0;
680                 if (noisy && ftp_debug == 0) {
681                         fputs(reply_string, ttyout);
682                         putc('\n', ttyout);
683                 }
684         }
685  cleanup_remotesize:
686         verbose = overbose;
687         return (size);
688 }
689
690 /*
691  * determine last modification time (in GMT) of remote file
692  */
693 time_t
694 remotemodtime(const char *file, int noisy)
695 {
696         int     overbose, ocode, r;
697         time_t  rtime;
698
699         overbose = verbose;
700         ocode = code;
701         rtime = -1;
702         if (ftp_debug == 0)
703                 verbose = -1;
704         if (! features[FEAT_MDTM]) {
705                 if (noisy)
706                         fprintf(ttyout,
707                             "MDTM is not supported by remote server.\n");
708                 goto cleanup_parse_time;
709         }
710         r = command("MDTM %s", file);
711         if (r == COMPLETE) {
712                 struct tm timebuf;
713                 char *timestr, *frac;
714
715                 /*
716                  * time-val = 14DIGIT [ "." 1*DIGIT ]
717                  *              YYYYMMDDHHMMSS[.sss]
718                  * mdtm-response = "213" SP time-val CRLF / error-response
719                  */
720                 timestr = reply_string + 4;
721
722                                         /*
723                                          * parse fraction.
724                                          * XXX: ignored for now
725                                          */
726                 frac = strchr(timestr, '\r');
727                 if (frac != NULL)
728                         *frac = '\0';
729                 frac = strchr(timestr, '.');
730                 if (frac != NULL)
731                         *frac++ = '\0';
732                 if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
733                         /*
734                          * XXX: Workaround for lame ftpd's that return
735                          *      `19100' instead of `2000'
736                          */
737                         fprintf(ttyout,
738             "Y2K warning! Incorrect time-val `%s' received from server.\n",
739                             timestr);
740                         timestr++;
741                         timestr[0] = '2';
742                         timestr[1] = '0';
743                         fprintf(ttyout, "Converted to `%s'\n", timestr);
744                 }
745                 memset(&timebuf, 0, sizeof(timebuf));
746                 if (strlen(timestr) != 14 ||
747                     (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) {
748  bad_parse_time:
749                         fprintf(ttyout, "Can't parse time `%s'.\n", timestr);
750                         goto cleanup_parse_time;
751                 }
752                 timebuf.tm_isdst = -1;
753                 rtime = timegm(&timebuf);
754                 if (rtime == -1) {
755                         if (noisy || ftp_debug != 0)
756                                 goto bad_parse_time;
757                         else
758                                 goto cleanup_parse_time;
759                 } else {
760                         DPRINTF("remotemodtime: parsed time `%s' as " LLF
761                             ", %s",
762                             timestr, (LLT)rtime,
763                             rfc2822time(localtime(&rtime)));
764                 }
765         } else {
766                 if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1)
767                         features[FEAT_MDTM] = 0;
768                 if (noisy && ftp_debug == 0) {
769                         fputs(reply_string, ttyout);
770                         putc('\n', ttyout);
771                 }
772         }
773  cleanup_parse_time:
774         verbose = overbose;
775         if (rtime == -1)
776                 code = ocode;
777         return (rtime);
778 }
779
780 /*
781  * Format tm in an RFC 2822 compatible manner, with a trailing \n.
782  * Returns a pointer to a static string containing the result.
783  */
784 const char *
785 rfc2822time(const struct tm *tm)
786 {
787         static char result[50];
788
789         if (strftime(result, sizeof(result),
790             "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0)
791                 errx(1, "Can't convert RFC 2822 time: buffer too small");
792         return result;
793 }
794
795 /*
796  * Parse HTTP-date as per RFC 2616.
797  * Return a pointer to the next character of the consumed date string,
798  * or NULL if failed.
799  */
800 const char *
801 parse_rfc2616time(struct tm *parsed, const char *httpdate)
802 {
803         const char *t;
804         const char *curlocale;
805
806         /* The representation of %a depends on the current locale. */
807         curlocale = setlocale(LC_TIME, NULL);
808         (void)setlocale(LC_TIME, "C");
809                                                                 /* RFC 1123 */
810         if ((t = strptime(httpdate, "%a, %d %b %Y %H:%M:%S GMT", parsed)) ||
811                                                                 /* RFC 850 */
812             (t = strptime(httpdate, "%a, %d-%b-%y %H:%M:%S GMT", parsed)) ||
813                                                                 /* asctime */
814             (t = strptime(httpdate, "%a, %b %d %H:%M:%S %Y", parsed))) {
815                 ;                       /* do nothing */
816         }
817         (void)setlocale(LC_TIME, curlocale);
818         return t;
819 }
820
821 /*
822  * Update global `localcwd', which contains the state of the local cwd
823  */
824 void
825 updatelocalcwd(void)
826 {
827
828         if (getcwd(localcwd, sizeof(localcwd)) == NULL)
829                 localcwd[0] = '\0';
830         DPRINTF("updatelocalcwd: got `%s'\n", localcwd);
831 }
832
833 /*
834  * Update global `remotecwd', which contains the state of the remote cwd
835  */
836 void
837 updateremotecwd(void)
838 {
839         int      overbose, ocode;
840         size_t   i;
841         char    *cp;
842
843         overbose = verbose;
844         ocode = code;
845         if (ftp_debug == 0)
846                 verbose = -1;
847         if (command("PWD") != COMPLETE)
848                 goto badremotecwd;
849         cp = strchr(reply_string, ' ');
850         if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
851                 goto badremotecwd;
852         cp += 2;
853         for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) {
854                 if (cp[0] == '"') {
855                         if (cp[1] == '"')
856                                 cp++;
857                         else
858                                 break;
859                 }
860                 remotecwd[i] = *cp;
861         }
862         remotecwd[i] = '\0';
863         DPRINTF("updateremotecwd: got `%s'\n", remotecwd);
864         goto cleanupremotecwd;
865  badremotecwd:
866         remotecwd[0]='\0';
867  cleanupremotecwd:
868         verbose = overbose;
869         code = ocode;
870 }
871
872 /*
873  * Ensure file is in or under dir.
874  * Returns 1 if so, 0 if not (or an error occurred).
875  */
876 int
877 fileindir(const char *file, const char *dir)
878 {
879         char    parentdirbuf[PATH_MAX+1], *parentdir;
880         char    realdir[PATH_MAX+1];
881         size_t  dirlen;
882
883                                         /* determine parent directory of file */
884         (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
885         parentdir = dirname(parentdirbuf);
886         if (strcmp(parentdir, ".") == 0)
887                 return 1;               /* current directory is ok */
888
889                                         /* find the directory */
890         if (realpath(parentdir, realdir) == NULL) {
891                 warn("Unable to determine real path of `%s'", parentdir);
892                 return 0;
893         }
894         if (realdir[0] != '/')          /* relative result is ok */
895                 return 1;
896         dirlen = strlen(dir);
897         if (strncmp(realdir, dir, dirlen) == 0 &&
898             (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
899                 return 1;
900         return 0;
901 }
902
903 /*
904  * List words in stringlist, vertically arranged
905  */
906 void
907 list_vertical(StringList *sl)
908 {
909         size_t i, j;
910         size_t columns, lines;
911         char *p;
912         size_t w, width;
913
914         width = 0;
915
916         for (i = 0 ; i < sl->sl_cur ; i++) {
917                 w = strlen(sl->sl_str[i]);
918                 if (w > width)
919                         width = w;
920         }
921         width = (width + 8) &~ 7;
922
923         columns = ttywidth / width;
924         if (columns == 0)
925                 columns = 1;
926         lines = (sl->sl_cur + columns - 1) / columns;
927         for (i = 0; i < lines; i++) {
928                 for (j = 0; j < columns; j++) {
929                         p = sl->sl_str[j * lines + i];
930                         if (p)
931                                 fputs(p, ttyout);
932                         if (j * lines + i + lines >= sl->sl_cur) {
933                                 putc('\n', ttyout);
934                                 break;
935                         }
936                         if (p) {
937                                 w = strlen(p);
938                                 while (w < width) {
939                                         w = (w + 8) &~ 7;
940                                         (void)putc('\t', ttyout);
941                                 }
942                         }
943                 }
944         }
945 }
946
947 /*
948  * Update the global ttywidth value, using TIOCGWINSZ.
949  */
950 void
951 setttywidth(int a)
952 {
953         struct winsize winsize;
954         int oerrno = errno;
955
956         if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
957             winsize.ws_col != 0)
958                 ttywidth = winsize.ws_col;
959         else
960                 ttywidth = 80;
961         errno = oerrno;
962 }
963
964 /*
965  * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
966  */
967 void
968 crankrate(int sig)
969 {
970
971         switch (sig) {
972         case SIGUSR1:
973                 if (rate_get)
974                         rate_get += rate_get_incr;
975                 if (rate_put)
976                         rate_put += rate_put_incr;
977                 break;
978         case SIGUSR2:
979                 if (rate_get && rate_get > rate_get_incr)
980                         rate_get -= rate_get_incr;
981                 if (rate_put && rate_put > rate_put_incr)
982                         rate_put -= rate_put_incr;
983                 break;
984         default:
985                 err(1, "crankrate invoked with unknown signal: %d", sig);
986         }
987 }
988
989
990 /*
991  * Setup or cleanup EditLine structures
992  */
993 #ifndef NO_EDITCOMPLETE
994 void
995 controlediting(void)
996 {
997         if (editing && el == NULL && hist == NULL) {
998                 HistEvent ev;
999                 int editmode;
1000
1001                 el = el_init(getprogname(), stdin, ttyout, stderr);
1002                 /* init editline */
1003                 hist = history_init();          /* init the builtin history */
1004                 history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
1005                 el_set(el, EL_HIST, history, hist);     /* use history */
1006
1007                 el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
1008                 el_set(el, EL_PROMPT, prompt);  /* set the prompt functions */
1009                 el_set(el, EL_RPROMPT, rprompt);
1010
1011                 /* add local file completion, bind to TAB */
1012                 el_set(el, EL_ADDFN, "ftp-complete",
1013                     "Context sensitive argument completion",
1014                     complete);
1015                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1016                 el_source(el, NULL);    /* read ~/.editrc */
1017                 if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
1018                         editing = 0;    /* the user doesn't want editing,
1019                                          * so disable, and let statement
1020                                          * below cleanup */
1021                 else
1022                         el_set(el, EL_SIGNAL, 1);
1023         }
1024         if (!editing) {
1025                 if (hist) {
1026                         history_end(hist);
1027                         hist = NULL;
1028                 }
1029                 if (el) {
1030                         el_end(el);
1031                         el = NULL;
1032                 }
1033         }
1034 }
1035 #endif /* !NO_EDITCOMPLETE */
1036
1037 /*
1038  * Convert the string `arg' to an int, which may have an optional SI suffix
1039  * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
1040  */
1041 int
1042 strsuftoi(const char *arg)
1043 {
1044         char *cp;
1045         long val;
1046
1047         if (!isdigit((unsigned char)arg[0]))
1048                 return (-1);
1049
1050         val = strtol(arg, &cp, 10);
1051         if (cp != NULL) {
1052                 if (cp[0] != '\0' && cp[1] != '\0')
1053                          return (-1);
1054                 switch (tolower((unsigned char)cp[0])) {
1055                 case '\0':
1056                 case 'b':
1057                         break;
1058                 case 'k':
1059                         val <<= 10;
1060                         break;
1061                 case 'm':
1062                         val <<= 20;
1063                         break;
1064                 case 'g':
1065                         val <<= 30;
1066                         break;
1067                 default:
1068                         return (-1);
1069                 }
1070         }
1071         if (val < 0 || val > INT_MAX)
1072                 return (-1);
1073
1074         return (val);
1075 }
1076
1077 /*
1078  * Set up socket buffer sizes before a connection is made.
1079  */
1080 void
1081 setupsockbufsize(int sock)
1082 {
1083
1084         if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
1085             (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1)
1086                 warn("Unable to set sndbuf size %d", sndbuf_size);
1087
1088         if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
1089             (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1)
1090                 warn("Unable to set rcvbuf size %d", rcvbuf_size);
1091 }
1092
1093 /*
1094  * Copy characters from src into dst, \ quoting characters that require it
1095  */
1096 void
1097 ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
1098 {
1099         size_t  di, si;
1100
1101         di = si = 0;
1102         while (src[si] != '\0' && di < dstlen && si < srclen) {
1103                 switch (src[si]) {
1104                 case '\\':
1105                 case ' ':
1106                 case '\t':
1107                 case '\r':
1108                 case '\n':
1109                 case '"':
1110                         /*
1111                          * Need room for two characters and NUL, avoiding
1112                          * incomplete escape sequences at end of dst.
1113                          */
1114                         if (di >= dstlen - 3)
1115                                 break;
1116                         dst[di++] = '\\';
1117                         /* FALLTHROUGH */
1118                 default:
1119                         dst[di] = src[si++];
1120                         if (di < dstlen)
1121                                 di++;
1122                 }
1123         }
1124         dst[di] = '\0';
1125 }
1126
1127 /*
1128  * Copy src into buf (which is len bytes long), expanding % sequences.
1129  */
1130 void
1131 formatbuf(char *buf, size_t len, const char *src)
1132 {
1133         const char      *p, *p2, *q;
1134         size_t           i;
1135         int              op, updirs, pdirs;
1136
1137 #define ADDBUF(x) do { \
1138                 if (i >= len - 1) \
1139                         goto endbuf; \
1140                 buf[i++] = (x); \
1141         } while (0)
1142
1143         p = src;
1144         for (i = 0; *p; p++) {
1145                 if (*p != '%') {
1146                         ADDBUF(*p);
1147                         continue;
1148                 }
1149                 p++;
1150
1151                 switch (op = *p) {
1152
1153                 case '/':
1154                 case '.':
1155                 case 'c':
1156                         p2 = connected ? remotecwd : "";
1157                         updirs = pdirs = 0;
1158
1159                         /* option to determine fixed # of dirs from path */
1160                         if (op == '.' || op == 'c') {
1161                                 int skip;
1162
1163                                 q = p2;
1164                                 while (*p2)             /* calc # of /'s */
1165                                         if (*p2++ == '/')
1166                                                 updirs++;
1167                                 if (p[1] == '0') {      /* print <x> or ... */
1168                                         pdirs = 1;
1169                                         p++;
1170                                 }
1171                                 if (p[1] >= '1' && p[1] <= '9') {
1172                                                         /* calc # to skip  */
1173                                         skip = p[1] - '0';
1174                                         p++;
1175                                 } else
1176                                         skip = 1;
1177
1178                                 updirs -= skip;
1179                                 while (skip-- > 0) {
1180                                         while ((p2 > q) && (*p2 != '/'))
1181                                                 p2--;   /* back up */
1182                                         if (skip && p2 > q)
1183                                                 p2--;
1184                                 }
1185                                 if (*p2 == '/' && p2 != q)
1186                                         p2++;
1187                         }
1188
1189                         if (updirs > 0 && pdirs) {
1190                                 if (i >= len - 5)
1191                                         break;
1192                                 if (op == '.') {
1193                                         ADDBUF('.');
1194                                         ADDBUF('.');
1195                                         ADDBUF('.');
1196                                 } else {
1197                                         ADDBUF('/');
1198                                         ADDBUF('<');
1199                                         if (updirs > 9) {
1200                                                 ADDBUF('9');
1201                                                 ADDBUF('+');
1202                                         } else
1203                                                 ADDBUF('0' + updirs);
1204                                         ADDBUF('>');
1205                                 }
1206                         }
1207                         for (; *p2; p2++)
1208                                 ADDBUF(*p2);
1209                         break;
1210
1211                 case 'M':
1212                 case 'm':
1213                         for (p2 = connected && hostname ? hostname : "-";
1214                             *p2 ; p2++) {
1215                                 if (op == 'm' && *p2 == '.')
1216                                         break;
1217                                 ADDBUF(*p2);
1218                         }
1219                         break;
1220
1221                 case 'n':
1222                         for (p2 = connected ? username : "-"; *p2 ; p2++)
1223                                 ADDBUF(*p2);
1224                         break;
1225
1226                 case '%':
1227                         ADDBUF('%');
1228                         break;
1229
1230                 default:                /* display unknown codes literally */
1231                         ADDBUF('%');
1232                         ADDBUF(op);
1233                         break;
1234
1235                 }
1236         }
1237  endbuf:
1238         buf[i] = '\0';
1239 }
1240
1241 /*
1242  * Determine if given string is an IPv6 address or not.
1243  * Return 1 for yes, 0 for no
1244  */
1245 int
1246 isipv6addr(const char *addr)
1247 {
1248         int rv = 0;
1249 #ifdef INET6
1250         struct addrinfo hints, *res;
1251
1252         memset(&hints, 0, sizeof(hints));
1253         hints.ai_family = AF_INET6;
1254         hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1255         hints.ai_flags = AI_NUMERICHOST;
1256         if (getaddrinfo(addr, "0", &hints, &res) != 0)
1257                 rv = 0;
1258         else {
1259                 rv = 1;
1260                 freeaddrinfo(res);
1261         }
1262         DPRINTF("isipv6addr: got %d for %s\n", rv, addr);
1263 #endif
1264         return (rv == 1) ? 1 : 0;
1265 }
1266
1267 /*
1268  * Read a line from the FILE stream into buf/buflen using fgets(), so up
1269  * to buflen-1 chars will be read and the result will be NUL terminated.
1270  * If the line has a trailing newline it will be removed.
1271  * If the line is too long, excess characters will be read until
1272  * newline/EOF/error.
1273  * If EOF/error occurs or a too-long line is encountered and errormsg
1274  * isn't NULL, it will be changed to a description of the problem.
1275  * (The EOF message has a leading \n for cosmetic purposes).
1276  * Returns:
1277  *      >=0     length of line (excluding trailing newline) if all ok
1278  *      -1      error occurred
1279  *      -2      EOF encountered
1280  *      -3      line was too long
1281  */
1282 int
1283 get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg)
1284 {
1285         int     rv, ch;
1286         size_t  len;
1287
1288         if (fgets(buf, buflen, stream) == NULL) {
1289                 if (feof(stream)) {     /* EOF */
1290                         rv = -2;
1291                         if (errormsg)
1292                                 *errormsg = "\nEOF received";
1293                 } else  {               /* error */
1294                         rv = -1;
1295                         if (errormsg)
1296                                 *errormsg = "Error encountered";
1297                 }
1298                 clearerr(stream);
1299                 return rv;
1300         }
1301         len = strlen(buf);
1302         if (buf[len-1] == '\n') {       /* clear any trailing newline */
1303                 buf[--len] = '\0';
1304         } else if (len == buflen-1) {   /* line too long */
1305                 while ((ch = getchar()) != '\n' && ch != EOF)
1306                         continue;
1307                 if (errormsg)
1308                         *errormsg = "Input line is too long";
1309                 clearerr(stream);
1310                 return -3;
1311         }
1312         if (errormsg)
1313                 *errormsg = NULL;
1314         return len;
1315 }
1316
1317 /*
1318  * Internal version of connect(2); sets socket buffer sizes,
1319  * binds to a specific local address (if set), and
1320  * supports a connection timeout using a non-blocking connect(2) with
1321  * a poll(2).
1322  * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
1323  * these will not be reverted on connection failure.
1324  * Returns 0 on success, or -1 upon failure (with an appropriate
1325  * error message displayed.)
1326  */
1327 int
1328 ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen)
1329 {
1330         int             flags, rv, timeout, error;
1331         socklen_t       slen;
1332         struct timeval  endtime, now, td;
1333         struct pollfd   pfd[1];
1334         char            hname[NI_MAXHOST];
1335         char            sname[NI_MAXSERV];
1336
1337         setupsockbufsize(sock);
1338         if (getnameinfo(name, namelen,
1339             hname, sizeof(hname), sname, sizeof(sname),
1340             NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
1341                 strlcpy(hname, "?", sizeof(hname));
1342                 strlcpy(sname, "?", sizeof(sname));
1343         }
1344
1345         if (bindai != NULL) {                   /* bind to specific addr */
1346                 struct addrinfo *ai;
1347
1348                 for (ai = bindai; ai != NULL; ai = ai->ai_next) {
1349                         if (ai->ai_family == name->sa_family)
1350                                 break;
1351                 }
1352                 if (ai == NULL)
1353                         ai = bindai;
1354                 if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
1355                         char    bname[NI_MAXHOST];
1356                         int     saveerr;
1357
1358                         saveerr = errno;
1359                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
1360                             bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0)
1361                                 strlcpy(bname, "?", sizeof(bname));
1362                         errno = saveerr;
1363                         warn("Can't bind to `%s'", bname);
1364                         return -1;
1365                 }
1366         }
1367
1368                                                 /* save current socket flags */
1369         if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
1370                 warn("Can't %s socket flags for connect to `%s:%s'",
1371                     "save", hname, sname);
1372                 return -1;
1373         }
1374                                                 /* set non-blocking connect */
1375         if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
1376                 warn("Can't set socket non-blocking for connect to `%s:%s'",
1377                     hname, sname);
1378                 return -1;
1379         }
1380
1381         /* NOTE: we now must restore socket flags on successful exit */
1382
1383         pfd[0].fd = sock;
1384         pfd[0].events = POLLIN|POLLOUT;
1385
1386         if (quit_time > 0) {                    /* want a non default timeout */
1387                 (void)gettimeofday(&endtime, NULL);
1388                 endtime.tv_sec += quit_time;    /* determine end time */
1389         }
1390
1391         rv = connect(sock, name, namelen);      /* inititate the connection */
1392         if (rv == -1) {                         /* connection error */
1393                 if (errno != EINPROGRESS) {     /* error isn't "please wait" */
1394  connecterror:
1395                         warn("Can't connect to `%s:%s'", hname, sname);
1396                         return -1;
1397                 }
1398
1399                                                 /* connect EINPROGRESS; wait */
1400                 do {
1401                         if (quit_time > 0) {    /* determine timeout */
1402                                 (void)gettimeofday(&now, NULL);
1403                                 timersub(&endtime, &now, &td);
1404                                 timeout = td.tv_sec * 1000 + td.tv_usec/1000;
1405                                 if (timeout < 0)
1406                                         timeout = 0;
1407                         } else {
1408                                 timeout = INFTIM;
1409                         }
1410                         pfd[0].revents = 0;
1411                         rv = ftp_poll(pfd, 1, timeout);
1412                                                 /* loop until poll ! EINTR */
1413                 } while (rv == -1 && errno == EINTR);
1414
1415                 if (rv == 0) {                  /* poll (connect) timed out */
1416                         errno = ETIMEDOUT;
1417                         goto connecterror;
1418                 }
1419
1420                 if (rv == -1) {                 /* poll error */
1421                         goto connecterror;
1422                 } else if (pfd[0].revents & (POLLIN|POLLOUT)) {
1423                         slen = sizeof(error);   /* OK, or pending error */
1424                         if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
1425                             &error, &slen) == -1) {
1426                                                 /* Solaris pending error */
1427                                 goto connecterror;
1428                         } else if (error != 0) {
1429                                 errno = error;  /* BSD pending error */
1430                                 goto connecterror;
1431                         }
1432                 } else {
1433                         errno = EBADF;          /* this shouldn't happen ... */
1434                         goto connecterror;
1435                 }
1436         }
1437
1438         if (fcntl(sock, F_SETFL, flags) == -1) {
1439                                                 /* restore socket flags */
1440                 warn("Can't %s socket flags for connect to `%s:%s'",
1441                     "restore", hname, sname);
1442                 return -1;
1443         }
1444         return 0;
1445 }
1446
1447 /*
1448  * Internal version of listen(2); sets socket buffer sizes first.
1449  */
1450 int
1451 ftp_listen(int sock, int backlog)
1452 {
1453
1454         setupsockbufsize(sock);
1455         return (listen(sock, backlog));
1456 }
1457
1458 /*
1459  * Internal version of poll(2), to allow reimplementation by select(2)
1460  * on platforms without the former.
1461  */
1462 int
1463 ftp_poll(struct pollfd *fds, int nfds, int timeout)
1464 {
1465         return poll(fds, nfds, timeout);
1466 }
1467
1468 /*
1469  * malloc() with inbuilt error checking
1470  */
1471 void *
1472 ftp_malloc(size_t size)
1473 {
1474         void *p;
1475
1476         p = malloc(size);
1477         if (p == NULL)
1478                 err(1, "Unable to allocate %ld bytes of memory", (long)size);
1479         return (p);
1480 }
1481
1482 /*
1483  * sl_init() with inbuilt error checking
1484  */
1485 StringList *
1486 ftp_sl_init(void)
1487 {
1488         StringList *p;
1489
1490         p = sl_init();
1491         if (p == NULL)
1492                 err(1, "Unable to allocate memory for stringlist");
1493         return (p);
1494 }
1495
1496 /*
1497  * sl_add() with inbuilt error checking
1498  */
1499 void
1500 ftp_sl_add(StringList *sl, char *i)
1501 {
1502
1503         sl_add(sl, i);
1504 }
1505
1506 /*
1507  * strdup() with inbuilt error checking
1508  */
1509 char *
1510 ftp_strdup(const char *str)
1511 {
1512         char *s;
1513
1514         if (str == NULL)
1515                 errx(1, "ftp_strdup: called with NULL argument");
1516         s = strdup(str);
1517         if (s == NULL)
1518                 err(1, "Unable to allocate memory for string copy");
1519         return (s);
1520 }