- Complete re-write of sasc.
[dragonfly.git] / crypto / heimdal / appl / push / push.c
1 /*
2  * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "push_locl.h"
35 RCSID("$Id: push.c,v 1.45 2001/09/04 09:45:52 assar Exp $");
36
37 #ifdef KRB4
38 static int use_v4 = -1;
39 #endif
40
41 #ifdef KRB5
42 static int use_v5 = -1;
43 static krb5_context context;
44 #endif
45
46 static char *port_str;
47 static int verbose_level;
48 static int do_fork;
49 static int do_leave;
50 static int do_version;
51 static int do_help;
52 static int do_from;
53 static int do_count;
54 static char *header_str;
55
56 struct getargs args[] = {
57 #ifdef KRB4
58     { "krb4",   '4', arg_flag,          &use_v4,        "Use Kerberos V4",
59       NULL },
60 #endif    
61 #ifdef KRB5
62     { "krb5",   '5', arg_flag,          &use_v5,        "Use Kerberos V5",
63       NULL },
64 #endif
65     { "verbose",'v', arg_counter,       &verbose_level, "Verbose",
66       NULL },
67     { "fork",   'f', arg_flag,          &do_fork,       "Fork deleting proc",
68       NULL },
69     { "leave",  'l', arg_flag,          &do_leave,      "Leave mail on server",
70       NULL },
71     { "port",   'p', arg_string,        &port_str,      "Use this port",
72       "number-or-service" },
73     { "from",    0,  arg_flag,          &do_from,       "Behave like from",
74       NULL },
75     { "headers", 0,  arg_string,        &header_str,    "Headers to print", NULL },
76     { "count", 'c',  arg_flag,          &do_count,      "Print number of messages", NULL},
77     { "version", 0,  arg_flag,          &do_version,    "Print version",
78       NULL },
79     { "help",    0,  arg_flag,          &do_help,       NULL,
80       NULL }
81
82 };
83
84 static void
85 usage (int ret)
86 {
87     arg_printusage (args,
88                     sizeof(args) / sizeof(args[0]),
89                     NULL,
90                     "[[{po:username[@hostname] | hostname[:username]}] ...] "
91                     "filename");
92     exit (ret);
93 }
94
95 static int
96 do_connect (const char *hostname, int port, int nodelay)
97 {
98     struct addrinfo *ai, *a;
99     struct addrinfo hints;
100     int error;
101     int s = -1;
102     char portstr[NI_MAXSERV];
103
104     memset (&hints, 0, sizeof(hints));
105     hints.ai_socktype = SOCK_STREAM;
106     hints.ai_protocol = IPPROTO_TCP;
107
108     snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
109
110     error = getaddrinfo (hostname, portstr, &hints, &ai);
111     if (error)
112         errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
113
114     for (a = ai; a != NULL; a = a->ai_next) {
115         s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
116         if (s < 0)
117             continue;
118         if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
119             warn ("connect(%s)", hostname);
120             close (s);
121             continue;
122         }
123         break;
124     }
125     freeaddrinfo (ai);
126     if (a == NULL) {
127         warnx ("failed to contact %s", hostname);
128         return -1;
129     }
130
131     if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
132                   (void *)&nodelay, sizeof(nodelay)) < 0)
133         err (1, "setsockopt TCP_NODELAY");
134     return s;
135 }
136
137 typedef enum { INIT = 0, GREET, USER, PASS, STAT, RETR, TOP, 
138                DELE, XDELE, QUIT} pop_state;
139
140 #define PUSH_BUFSIZ 65536
141
142 #define STEP 16
143
144 struct write_state {
145     struct iovec *iovecs;
146     size_t niovecs, maxiovecs, allociovecs;
147     int fd;
148 };
149
150 static void
151 write_state_init (struct write_state *w, int fd)
152 {
153 #ifdef UIO_MAXIOV
154     w->maxiovecs = UIO_MAXIOV;
155 #else
156     w->maxiovecs = 16;
157 #endif
158     w->allociovecs = min(STEP, w->maxiovecs);
159     w->niovecs = 0;
160     w->iovecs = emalloc(w->allociovecs * sizeof(*w->iovecs));
161     w->fd = fd;
162 }
163
164 static void
165 write_state_add (struct write_state *w, void *v, size_t len)
166 {
167     if(w->niovecs == w->allociovecs) {                          
168         if(w->niovecs == w->maxiovecs) {                                
169             if(writev (w->fd, w->iovecs, w->niovecs) < 0)               
170                 err(1, "writev");                               
171             w->niovecs = 0;                                     
172         } else {                                                
173             w->allociovecs = min(w->allociovecs + STEP, w->maxiovecs);  
174             w->iovecs = erealloc (w->iovecs,                            
175                                   w->allociovecs * sizeof(*w->iovecs)); 
176         }                                                       
177     }                                                           
178     w->iovecs[w->niovecs].iov_base = v;                         
179     w->iovecs[w->niovecs].iov_len  = len;                               
180     ++w->niovecs;                                                       
181 }
182
183 static void
184 write_state_flush (struct write_state *w)
185 {
186     if (w->niovecs) {
187         if (writev (w->fd, w->iovecs, w->niovecs) < 0)
188             err (1, "writev");
189         w->niovecs = 0;
190     }
191 }
192
193 static void
194 write_state_destroy (struct write_state *w)
195 {
196     free (w->iovecs);
197 }
198
199 static int
200 doit(int s,
201      const char *host,
202      const char *user,
203      const char *outfilename,
204      const char *header_str,
205      int leavep,
206      int verbose,
207      int forkp)
208 {
209     int ret;
210     char out_buf[PUSH_BUFSIZ];
211     int out_len = 0;
212     char in_buf[PUSH_BUFSIZ + 1];       /* sentinel */
213     size_t in_len = 0;
214     char *in_ptr = in_buf;
215     pop_state state = INIT;
216     unsigned count, bytes;
217     unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0;
218     unsigned sent_xdele = 0;
219     int out_fd;
220     char from_line[128];
221     size_t from_line_length;
222     time_t now;
223     struct write_state write_state;
224     int numheaders = 1;
225     char **headers = NULL;
226     int i;
227     char *tmp = NULL;
228
229     if (do_from) {
230         char *tmp2;
231
232         tmp2 = tmp = estrdup(header_str);
233
234         out_fd = -1;
235         if (verbose)
236             fprintf (stderr, "%s@%s\n", user, host);
237         while (*tmp != '\0') {
238             tmp = strchr(tmp, ',');
239             if (tmp == NULL)
240                 break;
241             tmp++;
242             numheaders++;
243         }
244
245         headers = emalloc(sizeof(char *) * (numheaders + 1));
246         for (i = 0; i < numheaders; i++) {
247             headers[i] = strtok_r(tmp2, ",", &tmp2);
248         }
249         headers[numheaders] = NULL;
250     } else {
251         out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666);
252         if (out_fd < 0)
253             err (1, "open %s", outfilename);
254         if (verbose)
255             fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename);
256     }
257
258     now = time(NULL);
259     from_line_length = snprintf (from_line, sizeof(from_line),
260                                  "From %s %s", "push", ctime(&now));
261
262     out_len = snprintf (out_buf, sizeof(out_buf),
263                         "USER %s\r\nPASS hej\r\nSTAT\r\n",
264                         user);
265     if (out_len < 0)
266         errx (1, "snprintf failed");
267     if (net_write (s, out_buf, out_len) != out_len)
268         err (1, "write");
269     if (verbose > 1)
270         write (STDERR_FILENO, out_buf, out_len);
271
272     if (!do_from)
273         write_state_init (&write_state, out_fd);
274
275     while(state != QUIT) {
276         fd_set readset, writeset;
277
278         FD_ZERO(&readset);
279         FD_ZERO(&writeset);
280         if (s >= FD_SETSIZE)
281             errx (1, "fd too large");
282         FD_SET(s,&readset);
283         if (((state == STAT || state == RETR || state == TOP)
284              && asked_for < count)
285             || (state == XDELE && !sent_xdele)
286             || (state == DELE && asked_deleted < count))
287             FD_SET(s,&writeset);
288         ret = select (s + 1, &readset, &writeset, NULL, NULL);
289         if (ret < 0) {
290             if (errno == EAGAIN)
291                 continue;
292             else
293                 err (1, "select");
294         }
295         
296         if (FD_ISSET(s, &readset)) {
297             char *beg, *p;
298             size_t rem;
299             int blank_line = 0;
300             
301             ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1);
302             if (ret < 0)
303                 err (1, "read");
304             else if (ret == 0)
305                 errx (1, "EOF during read");
306             
307             in_len += ret;
308             in_ptr += ret;
309             *in_ptr = '\0';
310             
311             beg = in_buf;
312             rem = in_len;
313             while(rem > 1
314                   && (p = strstr(beg, "\r\n")) != NULL) {
315                 if (state == TOP) {
316                     char *copy = beg;
317
318                     for (i = 0; i < numheaders; i++) {
319                         size_t len;
320
321                         len = min(p - copy + 1, strlen(headers[i]));
322                         if (strncasecmp(copy, headers[i], len) == 0) {
323                             fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
324                         }
325                     }
326                     if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
327                         if (numheaders > 1)
328                             fprintf (stdout, "\n");
329                         state = STAT;
330                         if (++retrieved == count) {
331                             state = QUIT;
332                             net_write (s, "QUIT\r\n", 6);
333                             if (verbose > 1)
334                                 net_write (STDERR_FILENO, "QUIT\r\n", 6);
335                         }
336                     }
337                     rem -= p - beg + 2;
338                     beg = p + 2;
339                 } else if (state == RETR) {
340                     char *copy = beg;
341                     if (beg[0] == '.') {
342                         if (beg[1] == '\r' && beg[2] == '\n') {
343                             if(!blank_line)
344                                 write_state_add(&write_state, "\n", 1);
345                             state = STAT;
346                             rem -= p - beg + 2;
347                             beg = p + 2;
348                             if (++retrieved == count) {
349                                 write_state_flush (&write_state);
350                                 if (fsync (out_fd) < 0)
351                                     err (1, "fsync");
352                                 close(out_fd);
353                                 if (leavep) {
354                                     state = QUIT;
355                                     net_write (s, "QUIT\r\n", 6);
356                                     if (verbose > 1)
357                                         net_write (STDERR_FILENO, "QUIT\r\n", 6);
358                                 } else {
359                                     if (forkp) {
360                                         pid_t pid;
361
362                                         pid = fork();
363                                         if (pid < 0)
364                                             warn ("fork");
365                                         else if(pid != 0) {
366                                             if(verbose)
367                                                 fprintf (stderr,
368                                                          "(exiting)");
369                                             return 0;
370                                         }
371                                     }
372
373                                     state = XDELE;
374                                     if (verbose)
375                                         fprintf (stderr, "deleting... ");
376                                 }
377                             }
378                             continue;
379                         } else
380                             ++copy;
381                     }
382                     *p = '\n';
383                     if(blank_line && 
384                        strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
385                         write_state_add(&write_state, ">", 1);
386                     write_state_add(&write_state, copy, p - copy + 1);
387                     blank_line = (*copy == '\n');
388                     rem -= p - beg + 2;
389                     beg = p + 2;
390                 } else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
391                     if (state == STAT) {
392                         if (!do_from)
393                             write_state_add(&write_state,
394                                             from_line, from_line_length);
395                         blank_line = 0;
396                         if (do_from) 
397                             state = TOP;
398                         else
399                             state = RETR;
400                     } else if (state == XDELE) {
401                         state = QUIT;
402                         net_write (s, "QUIT\r\n", 6);
403                         if (verbose > 1)
404                             net_write (STDERR_FILENO, "QUIT\r\n", 6);
405                         break;
406                     } else if (state == DELE) {
407                         if (++deleted == count) {
408                             state = QUIT;
409                             net_write (s, "QUIT\r\n", 6);
410                             if (verbose > 1)
411                                 net_write (STDERR_FILENO, "QUIT\r\n", 6);
412                             break;
413                         }
414                     } else if (++state == STAT) {
415                         if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
416                             errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
417                         if (verbose) {
418                             fprintf (stderr, "%u message(s) (%u bytes). "
419                                      "fetching... ",
420                                      count, bytes);
421                             if (do_from)
422                                 fprintf (stderr, "\n");
423                         } else if (do_count) {
424                             fprintf (stderr, "%u message(s) (%u bytes).\n",
425                                      count, bytes);
426                         }
427                         if (count == 0) {
428                             state = QUIT;
429                             net_write (s, "QUIT\r\n", 6);
430                             if (verbose > 1)
431                                 net_write (STDERR_FILENO, "QUIT\r\n", 6);
432                             break;
433                         }
434                     }
435
436                     rem -= p - beg + 2;
437                     beg = p + 2;
438                 } else {
439                     if(state == XDELE) {
440                         state = DELE;
441                         rem -= p - beg + 2;
442                         beg = p + 2;
443                     } else
444                         errx (1, "Bad response: %.*s", (int)(p - beg), beg);
445                 }
446             }
447             if (!do_from)
448                 write_state_flush (&write_state);
449
450             memmove (in_buf, beg, rem);
451             in_len = rem;
452             in_ptr = in_buf + rem;
453         }
454         if (FD_ISSET(s, &writeset)) {
455             if ((state == STAT && !do_from) || state == RETR)
456                 out_len = snprintf (out_buf, sizeof(out_buf),
457                                     "RETR %u\r\n", ++asked_for);
458             else if ((state == STAT && do_from) || state == TOP)
459                 out_len = snprintf (out_buf, sizeof(out_buf),
460                                     "TOP %u 0\r\n", ++asked_for);
461             else if(state == XDELE) {
462                 out_len = snprintf(out_buf, sizeof(out_buf),
463                                    "XDELE %u %u\r\n", 1, count);
464                 sent_xdele++;
465             }
466             else if(state == DELE)
467                 out_len = snprintf (out_buf, sizeof(out_buf),
468                                     "DELE %u\r\n", ++asked_deleted);
469             if (out_len < 0)
470                 errx (1, "snprintf failed");
471             if (net_write (s, out_buf, out_len) != out_len)
472                 err (1, "write");
473             if (verbose > 1)
474                 write (STDERR_FILENO, out_buf, out_len);
475         }
476     }
477     if (verbose)
478         fprintf (stderr, "Done\n");
479     if (do_from) {
480         free (tmp);
481         free (headers);
482     } else {
483         write_state_destroy (&write_state);
484     }
485     return 0;
486 }
487
488 #ifdef KRB5
489 static int
490 do_v5 (const char *host,
491        int port,
492        const char *user,
493        const char *filename,
494        const char *header_str,
495        int leavep,
496        int verbose,
497        int forkp)
498 {
499     krb5_error_code ret;
500     krb5_auth_context auth_context = NULL;
501     krb5_principal server;
502     int s;
503
504     s = do_connect (host, port, 1);
505     if (s < 0)
506         return 1;
507
508     ret = krb5_sname_to_principal (context,
509                                    host,
510                                    "pop",
511                                    KRB5_NT_SRV_HST,
512                                    &server);
513     if (ret) {
514         warnx ("krb5_sname_to_principal: %s",
515                krb5_get_err_text (context, ret));
516         return 1;
517     }
518
519     ret = krb5_sendauth (context,
520                          &auth_context,
521                          &s,
522                          "KPOPV1.0",
523                          NULL,
524                          server,
525                          0,
526                          NULL,
527                          NULL,
528                          NULL,
529                          NULL,
530                          NULL,
531                          NULL);
532     krb5_free_principal (context, server);
533     if (ret) {
534         warnx ("krb5_sendauth: %s",
535                krb5_get_err_text (context, ret));
536         return 1;
537     }
538     return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
539 }
540 #endif
541
542 #ifdef KRB4
543 static int
544 do_v4 (const char *host,
545        int port,
546        const char *user,
547        const char *filename,
548        const char *header_str,
549        int leavep,
550        int verbose,
551        int forkp)
552 {
553     KTEXT_ST ticket;
554     MSG_DAT msg_data;
555     CREDENTIALS cred;
556     des_key_schedule sched;
557     int s;
558     int ret;
559
560     s = do_connect (host, port, 1);
561     if (s < 0)
562         return 1;
563     ret = krb_sendauth(0,
564                        s,
565                        &ticket, 
566                        "pop",
567                        (char *)host,
568                        krb_realmofhost(host),
569                        getpid(),
570                        &msg_data,
571                        &cred,
572                        sched,
573                        NULL,
574                        NULL,
575                        "KPOPV0.1");
576     if(ret) {
577         warnx("krb_sendauth: %s", krb_get_err_text(ret));
578         return 1;
579     }
580     return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
581 }
582 #endif /* KRB4 */
583
584 #ifdef HESIOD
585
586 #ifdef HESIOD_INTERFACES
587
588 static char *
589 hesiod_get_pobox (const char **user)
590 {
591     void *context;
592     struct hesiod_postoffice *hpo;
593     char *ret = NULL;
594
595     if(hesiod_init (&context) != 0)
596         err (1, "hesiod_init");
597
598     hpo = hesiod_getmailhost (context, *user);
599     if (hpo == NULL) {
600         warn ("hesiod_getmailhost %s", *user);
601     } else {
602         if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
603             errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
604
605         ret = estrdup(hpo->hesiod_po_host);
606         *user = estrdup(hpo->hesiod_po_name);
607         hesiod_free_postoffice (context, hpo);
608     }
609     hesiod_end (context);
610     return ret;
611 }
612
613 #else /* !HESIOD_INTERFACES */
614
615 static char *
616 hesiod_get_pobox (const char **user)
617 {
618     char *ret = NULL;
619     struct hes_postoffice *hpo;
620
621     hpo = hes_getmailhost (*user);
622     if (hpo == NULL) {
623         warn ("hes_getmailhost %s", *user);
624     } else {
625         if (strcasecmp(hpo->po_type, "pop") != 0)
626             errx (1, "Unsupported po type %s", hpo->po_type);
627
628         ret = estrdup(hpo->po_host);
629         *user = estrdup(hpo->po_name);
630     }
631     return ret;
632 }
633
634 #endif /* HESIOD_INTERFACES */
635
636 #endif /* HESIOD */
637
638 static char *
639 get_pobox (const char **user)
640 {
641     char *ret = NULL;
642
643 #ifdef HESIOD
644     ret = hesiod_get_pobox (user);
645 #endif
646
647     if (ret == NULL)
648         ret = getenv("MAILHOST");
649     if (ret == NULL)
650         errx (1, "MAILHOST not set");
651     return ret;
652 }
653
654 static void
655 parse_pobox (char *a0, const char **host, const char **user)
656 {
657     const char *h, *u;
658     char *p;
659     int po = 0;
660
661     if (a0 == NULL) {
662
663         *user = getenv ("USERNAME");
664         if (*user == NULL) {
665             struct passwd *pwd = getpwuid (getuid ());
666
667             if (pwd == NULL)
668                 errx (1, "Who are you?");
669             *user = estrdup (pwd->pw_name);
670         }
671         *host = get_pobox (user);
672         return;
673     }
674
675     /* if the specification starts with po:, remember this information */
676     if(strncmp(a0, "po:", 3) == 0) {
677         a0 += 3;
678         po++;
679     }
680     /* if there is an `@', the hostname is after it, otherwise at the
681        beginning of the string */
682     p = strchr(a0, '@');
683     if(p != NULL) {
684         *p++ = '\0';
685         h = p;
686     } else {
687         h = a0;
688     }
689     /* if there is a `:', the username comes before it, otherwise at
690        the beginning of the string */
691     p = strchr(a0, ':');
692     if(p != NULL) {
693         *p++ = '\0';
694         u = p;
695     } else {
696         u = a0;
697     }
698     if(h == u) {
699         /* some inconsistent compatibility with various mailers */
700         if(po) {
701             h = get_pobox (&u);
702         } else {
703             u = get_default_username ();
704             if (u == NULL)
705                 errx (1, "Who are you?");
706         }
707     }
708     *host = h;
709     *user = u;
710 }
711
712 int
713 main(int argc, char **argv)
714 {
715     int port = 0;
716     int optind = 0;
717     int ret = 1;
718     const char *host, *user, *filename = NULL;
719     char *pobox = NULL;
720
721     setprogname (argv[0]);
722
723 #ifdef KRB5
724     {
725         krb5_error_code ret;
726
727         ret = krb5_init_context (&context);
728         if (ret)
729             errx (1, "krb5_init_context failed: %d", ret);
730     }
731 #endif
732
733     if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
734                 &optind))
735         usage (1);
736
737     argc -= optind;
738     argv += optind;
739
740 #if defined(KRB4) && defined(KRB5)
741     if(use_v4 == -1 && use_v5 == 1)
742         use_v4 = 0;
743     if(use_v5 == -1 && use_v4 == 1)
744         use_v5 = 0;
745 #endif    
746
747     if (do_help)
748         usage (0);
749
750     if (do_version) {
751         print_version(NULL);
752         return 0;
753     }
754         
755     if (do_from && header_str == NULL)
756         header_str = "From:";
757     else if (header_str != NULL)
758         do_from = 1;
759
760     if (do_from) {
761         if (argc == 0)
762             pobox = NULL;
763         else if (argc == 1)
764             pobox = argv[0];
765         else
766             usage (1);
767     } else {
768         if (argc == 1) {
769             filename = argv[0];
770             pobox    = NULL;
771         } else if (argc == 2) {
772             filename = argv[1];
773             pobox    = argv[0];
774         } else
775             usage (1);
776     }
777
778     if (port_str) {
779         struct servent *s = roken_getservbyname (port_str, "tcp");
780
781         if (s)
782             port = s->s_port;
783         else {
784             char *ptr;
785
786             port = strtol (port_str, &ptr, 10);
787             if (port == 0 && ptr == port_str)
788                 errx (1, "Bad port `%s'", port_str);
789             port = htons(port);
790         }
791     }
792     if (port == 0) {
793 #ifdef KRB5
794         port = krb5_getportbyname (context, "kpop", "tcp", 1109);
795 #elif defined(KRB4)
796         port = k_getportbyname ("kpop", "tcp", htons(1109));
797 #else
798 #error must define KRB4 or KRB5
799 #endif
800     }
801
802     parse_pobox (pobox, &host, &user);
803
804 #ifdef KRB5
805     if (ret && use_v5) {
806         ret = do_v5 (host, port, user, filename, header_str,
807                      do_leave, verbose_level, do_fork);
808     }
809 #endif
810
811 #ifdef KRB4
812     if (ret && use_v4) {
813         ret = do_v4 (host, port, user, filename, header_str,
814                      do_leave, verbose_level, do_fork);
815     }
816 #endif /* KRB4 */
817     return ret;
818 }