1 /* Common functions for each GNATS client.
2 Copyright (C) 1994, 1995, 1999, 2000, 2002 Free Software Foundation, Inc.
3 Copyright (C) 2001 Dan Martinez
4 Contributed by Brendan Kehoe (brendan@cygnus.com).
5
6 This file is part of GNU GNATS.
7
8 GNU GNATS is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 GNU GNATS is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more detailmails.
17
18 You should have received a copy of the GNU General Public License
19 along with GNU GNATS; see the file COPYING. If not, write to the Free
20 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
21
22 #include "gnats.h"
23 #include "gnatsd.h"
24 #include "query.h"
25 #include <sys/wait.h>
26
27 #ifdef HAVE_MACHINE_ENDIAN_H
28 #include <machine/endian.h> /* for htons */
29 #endif
30
31 #ifdef HAVE_KERBEROS
32 #include <des.h>
33 #include <krb.h>
34 #ifndef HAVE_KRB_GET_ERR_TEXT
35 #define krb_get_err_text(status) krb_err_txt[status]
36 #endif
37 int remember_krb_auth_ret = 0; /* yes, I like lisp. go away. */
38 #endif
39
40 /* For the GNATS server we'll be talking with. */
41 static FILE *serv_write, *serv_read;
42
43 /* If we're reading in a PR, this is it. */
44 static PR *prBeingRead = NULL;
45
46 /* If we're reading in a list of PRs, this is where it goes. */
47 static StringList **prList;
48
49 /* The descriptor for talking to the server in non-stdio ways. */
50 static int sockfd, dupfd;
51
52 /* If 1, send information as we make the query. */
53 int debug = 0;
54
55 static int forkedGnatsd = 0;
56
57 static void
free_reply(Reply * r)58 free_reply (Reply *r)
59 {
60 free (r->text);
61 free ((char*)r);
62 }
63
64
65 static Reply *
server_reply(void)66 server_reply (void)
67 {
68 char *recvline;
69 char *p, c;
70 Reply *res = NULL;
71
72 recvline = read_line (serv_read, NULL);
73
74 if (recvline == NULL)
75 {
76 fprintf (stderr, "%s: error reading from server\n", program_name);
77 /* This is probably not a good idea, eh? XXX ??? !!! */
78 safe_exit ();
79 }
80
81 c = recvline[0];
82 if (isascii ((int) c) && isdigit((int) c))
83 {
84 for (p = &recvline[0];
85 *p && isascii ((int) *p) && isdigit ((int) *p);
86 p++)
87 continue;
88 if (*p)
89 {
90 size_t i;
91 res = (Reply *) xmalloc (sizeof (Reply));
92
93 if (*p == '-')
94 {
95 res->type = REPLY_CONT;
96 }
97 else
98 {
99 if (*p != ' ')
100 {
101 fprintf (stderr, "%s: bad type of reply from server\n",
102 program_name);
103 }
104 res->type = REPLY_END;
105 }
106 *p = '\0';
107 res->state = atoi (recvline);
108 res->text = xstrdup (p + 1);
109 i = strlen (res->text) - 2;
110 if (res->text[i] == '\r' || res->text[i] == '\n')
111 {
112 res->text[i] = '\0';
113 }
114 else if (res->text[i + 1] == '\n')
115 {
116 res->text[i + 1] = '\0';
117 }
118 }
119 }
120
121 free (recvline);
122 return res;
123 }
124
125 static void
readPRFromServer(PR * pr)126 readPRFromServer (PR *pr)
127 {
128 char *recvline;
129 int notdone = 1;
130
131 while (notdone && ((recvline = read_line (serv_read, NULL)) != NULL))
132 {
133 if (debug)
134 {
135 fprintf (stderr, "%s: received `%s'\n", program_name, recvline);
136 }
137
138 if (memcmp (recvline, ".\r\n\0", 4) == 0)
139 {
140 finishReadPR (pr, 0);
141 notdone = 0;
142 }
143 else
144 {
145 size_t linelen = strlen (recvline);
146 int i;
147
148 if (linelen > 0)
149 {
150 if (recvline[linelen - 2] == '\r')
151 {
152 /* Only correct this if we're actually at the end of a
153 line. */
154 recvline[linelen - 2] = '\n';
155 recvline[linelen - 1] = '\0';
156 linelen--;
157 }
158 }
159 /* Remove escape character added by write_multiline() */
160 i = 0;
161 if (recvline[0] == '.')
162 {
163 i = 1;
164 }
165 addLineToPR (pr, recvline, recvline + i, linelen - i, 0);
166 recvline = NULL;
167 }
168 if (recvline != NULL)
169 {
170 free (recvline);
171 }
172 }
173 }
174
175 static void
readPRListFromServer(void)176 readPRListFromServer (void)
177 {
178 char *recvline;
179 int notdone = 1;
180
181 while (notdone && ((recvline = read_line (serv_read, NULL)) != NULL))
182 {
183 if (debug)
184 {
185 fprintf (stderr, "%s: received `%s'\n", program_name, recvline);
186 }
187
188 if (memcmp (recvline, ".\r\n\0", 4) == 0)
189 {
190 notdone = 0;
191 }
192 else
193 {
194 size_t linelen = strlen (recvline);
195 int i;
196
197 if (linelen > 0)
198 {
199 if (recvline[linelen - 2] == '\r')
200 {
201 /* Only correct this if we're actually at the end of a
202 line. */
203 recvline[linelen - 2] = '\n';
204 recvline[linelen - 1] = '\0';
205 linelen--;
206 }
207 }
208 /* Remove escape character added by write_multiline() */
209 i = 0;
210 if (recvline[0] == '.')
211 {
212 i = 1;
213 }
214 while (recvline[i] != '\0' && isspace ((int)(unsigned char) recvline[i]))
215 {
216 i++;
217 }
218 if (isdigit ((int) recvline[i]))
219 {
220 *prList = new_string_list_ent (xstrdup (recvline + i), NULL);
221 prList = &((*prList)->next);
222 }
223 }
224 }
225 }
226
227 static void
read_server(FILE * outfp)228 read_server (FILE *outfp)
229 {
230 char *recvline;
231 int notdone = 1;
232
233 while (notdone && ((recvline = read_line (serv_read, NULL)) != NULL))
234 {
235 if (debug)
236 {
237 fprintf (stderr, "%s: received `%s'\n", program_name, recvline);
238 }
239
240 if (memcmp (recvline, ".\r\n\0", 4) == 0)
241 {
242 notdone = 0;
243 }
244 else
245 {
246 int i;
247 if (recvline[1])
248 {
249 size_t linelen = strlen (recvline) - 2;
250 if (recvline[linelen] == '\r')
251 {
252 /* Only correct this if we're actually at the end of a
253 line. */
254 recvline[linelen] = '\n';
255 recvline[linelen + 1] = '\0';
256 }
257 }
258 /* Remove escape character added by write_multiline() */
259 i = 0;
260 if (recvline[0] == '.')
261 {
262 i = 1;
263 }
264 fprintf (outfp, "%s", recvline + i);
265 }
266 free (recvline);
267 }
268 }
269
270
271 /* Return the code of the response from the server, or 0 if there wasn't
272 any. */
273 int
get_reply(FILE * outfp)274 get_reply (FILE *outfp)
275 {
276 Reply *r;
277 int done = 0;
278 int retval = 0;
279
280 /* Make sure anything we've written has gone to them. */
281 if (fflush (serv_write) == EOF || ferror (serv_write))
282 {
283 fprintf (stderr, "%s: error writing to server\n", program_name);
284 }
285
286 while (! done)
287 {
288 r = server_reply ();
289
290 if (r != NULL)
291 {
292 if (r->type != REPLY_CONT)
293 {
294 done = 1;
295 }
296
297 if (debug)
298 {
299 fprintf (stderr, "%s: received %d `%s'\n",
300 program_name, r->state, r->text);
301 }
302
303 switch (r->state)
304 {
305 case CODE_GREETING:
306 case CODE_OK:
307 case CODE_SEND_PR:
308 case CODE_SEND_TEXT:
309 case CODE_SEND_CHANGE_REASON:
310 case CODE_INFORMATION_FILLER:
311 break;
312
313 case CODE_CLOSING:
314 if (debug)
315 {
316 fprintf (stderr, "%s: server closing down\n", program_name);
317 }
318 break;
319
320 case CODE_PR_READY:
321 case CODE_TEXT_READY:
322 /* read it til we get a . */
323 if (prBeingRead != NULL)
324 {
325 readPRFromServer (prBeingRead);
326 }
327 else if (prList != NULL)
328 {
329 readPRListFromServer ();
330 }
331 else
332 {
333 if (outfp != NULL)
334 read_server (outfp);
335 else
336 {
337 fprintf (stderr, "%s: unexpected %d received\n",
338 program_name, r->state);
339 safe_exit();
340 }
341 }
342 break;
343
344 case CODE_INFORMATION:
345 if (outfp != NULL)
346 fprintf (outfp, "%s\n", r->text);
347 break;
348
349 case CODE_NONEXISTENT_PR:
350 case CODE_UNREADABLE_PR:
351 case CODE_LOCKED_PR:
352 case CODE_FILE_ERROR:
353 case CODE_ERROR:
354 case CODE_NO_ADM_ENTRY:
355 case CODE_INVALID_FIELD_NAME:
356 case CODE_INVALID_PR_CONTENTS:
357 case CODE_INVALID_ENUM:
358 case CODE_INVALID_DATE:
359 case CODE_INVALID_EXPR:
360 case CODE_INVALID_DATABASE:
361 case CODE_INVALID_FIELD_EDIT:
362 fprintf (stderr, "%s: %s\n", program_name, r->text);
363 safe_exit ();
364 break;
365
366 case CODE_PR_NOT_LOCKED:
367 fprintf (stderr, "%s: PR is not locked\n", program_name);
368 safe_exit ();
369 break;
370
371 case CODE_GNATS_LOCKED:
372 fprintf (stderr, "%s: database is locked\n", program_name);
373 safe_exit ();
374 break;
375
376 case CODE_NO_PRS_MATCHED:
377 fprintf (stderr, "%s: no PRs matched\n", program_name);
378 safe_exit ();
379 break;
380
381 case CODE_NO_KERBEROS:
382 fprintf (stderr,
383 "%s: no Kerberos support, authentication failed\n",
384 program_name);
385 /* FALLTHROUGH */
386 case CODE_NO_ACCESS:
387 #ifdef HAVE_KERBEROS
388 if (remember_krb_auth_ret)
389 fprintf (stderr, "%s: error (%s) while attempting krb4-auth\n",
390 program_name,
391 krb_get_err_text (remember_krb_auth_ret));
392 else
393 fprintf (stderr, "%s: access denied\n", program_name);
394 #else
395 fprintf (stderr, "%s: access denied\n", program_name);
396 #endif
397 safe_exit ();
398 break;
399
400 default:
401 fprintf (stderr, "%s: cannot understand %d `%s'\n",
402 program_name, r->state, r->text);
403 safe_exit ();
404 break;
405 }
406 retval = r->state;
407
408 free_reply (r);
409 }
410 else
411 {
412 if (debug)
413 {
414 fprintf (stderr, "%s: null reply from the server\n",
415 program_name);
416 }
417 retval = 0;
418 done = 1;
419 }
420 }
421
422 return retval;
423 }
424
425 void
safe_exit(void)426 safe_exit (void)
427 {
428 static int doing_exit = 0;
429
430 if (! doing_exit)
431 {
432 doing_exit = 1;
433 client_exit ();
434 exit (1);
435 }
436 }
437
438 #ifdef HAVE_KERBEROS
439
440 void
tohex(char * ptr,char * buf,int sz)441 tohex (char *ptr, char *buf, int sz)
442 {
443 static const char hex[] = "0123456789abcdef";
444 int c;
445 while (sz--)
446 {
447 c = *ptr++ & 0xff;
448 *buf++ = hex[c >> 4];
449 *buf++ = hex[c & 15];
450 }
451 *buf++ = 0;
452 }
453
454 int
hexdigit(char c)455 hexdigit (char c)
456 {
457 if (c >= '0' && c <= '9')
458 return c - '0';
459 if (c >= 'a' && c <= 'f')
460 return c - 'a' + 10;
461 if (c >= 'A' && c <= 'F')
462 return c - 'A' + 10;
463 return -1;
464 }
465
466 void
fromhex(char * ptr,char * buf,int sz)467 fromhex (char *ptr, char *buf, int sz)
468 {
469 int val;
470 while (sz--)
471 {
472 val = hexdigit (*buf++);
473 val <<= 4;
474 val |= hexdigit (*buf++);
475 *ptr++ = val;
476 }
477 }
478 #endif
479
480 static int
clientConnect(ErrorDesc * err,const char * hostname,int port)481 clientConnect (ErrorDesc *err, const char *hostname, int port)
482 {
483 FILE *file;
484
485 #if 1
486 if (hostname[0] == '/' || hostname[0] == '.')
487 {
488 int sockets[2];
489
490 setuid (getuid ());
491 setgid (getgid ());
492 socketpair (PF_UNIX, SOCK_STREAM, 0, sockets);
493
494 forkedGnatsd = fork ();
495 if (forkedGnatsd == 0)
496 {
497 unsetenv ("GNATSDB");
498 close (0);
499 close (1);
500 dup (sockets[0]);
501 dup (sockets[0]);
502 execl (hostname, hostname, "--not-inetd", NULL);
503 _exit (1);
504 }
505 else
506 {
507 sockfd = sockets[1];
508 }
509 }
510 else
511 #endif
512 {
513 struct sockaddr_in server;
514 struct hostent *host;
515
516 memset ((char *) &server, 0, sizeof (server));
517 server.sin_family = AF_INET;
518 if (isdigit ((int) hostname[0]))
519 {
520 server.sin_addr.s_addr = inet_addr (hostname);
521 }
522 else
523 {
524 host = gethostbyname (hostname);
525 if (host == NULL)
526 {
527 setError (err, CODE_INVALID_DATABASE, "%s: No such host",
528 hostname);
529 return -1;
530 }
531 memcpy (&server.sin_addr, host->h_addr, sizeof (server.sin_addr));
532 }
533
534 server.sin_port = htons (port);
535
536 if (debug)
537 {
538 fprintf (stderr, "opening connection to %s\n", hostname);
539 }
540
541 sockfd = socket (AF_INET, SOCK_STREAM, 0);
542 if (sockfd < 0)
543 {
544 setError (err, CODE_ERROR, "could not open a socket");
545 return -1;
546 }
547 if (connect (sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
548 {
549 setError (err, CODE_ERROR,
550 "Couldn't connect to server on %s, port %d",
551 hostname, port);
552 close (sockfd);
553 return -1;
554 }
555 }
556 if ((file = fdopen (sockfd, "r")) == NULL)
557 {
558 setError (err, CODE_ERROR, "read fdopen() failed");
559 close (sockfd);
560 return -1;
561 }
562 serv_read = file;
563 dupfd = dup (sockfd);
564 if (dupfd < 0)
565 {
566 setError (err, CODE_ERROR, "dup() failed");
567 close (sockfd);
568 return -1;
569 }
570 if ((file = fdopen (dupfd, "w")) == NULL)
571 {
572 setError (err, CODE_ERROR, "write fdopen() failed");
573 close (sockfd);
574 close (dupfd);
575 return -1;
576 }
577 serv_write = file;
578
579 /* Get the hello. */
580 get_reply (stdout);
581
582 #ifdef HAVE_KERBEROS
583 {
584 /* This is ugly, because krb_sendauth/krb_recvauth don't maintain
585 a sensible connection state if authentication fails. This is
586 unacceptable here. So I've broken out most of the interesting
587 bits of those routines here. I've also added a hex encoding of
588 the data exchanged instead of the standard authenticator
589 format, to maintain the text-only protocol used in GNATS. */
590
591 Reply *r;
592 char *realm, *p, *me;
593 int ret, i;
594 struct sockaddr_in laddr;
595 int laddrlen = sizeof (laddr);
596 char hname[MAXHOSTNAMELEN]; /* for canonicalization by krb_mk_auth */
597 /* Scratch space for Kerberos library, required to be provided by
598 the application. Keep it all together so we can erase it all
599 at once later. */
600 struct {
601 CREDENTIALS cred;
602 MSG_DAT msg_data;
603 Key_schedule sched;
604 KTEXT_ST tkt, packet;
605 char buf[MAX_KTXT_LEN * 2 + 10];
606 } k;
607 k.packet.mbz = 0;
608
609 #define PUNT(reason,subreason) do{if (debug) fprintf(stderr,"%s: %s (%s)\n", program_name, reason, subreason);goto krb_exit;}while(0)
610 #define SKIP(reason) PUNT("skipping Kerberos authentication",reason)
611 #define FAIL(reason) PUNT("Kerberos authentication failed", reason)
612
613 /* This probably ought to be a crypto checksum of the
614 authenticator, but a constant should be nearly as secure. */
615 #define CKSUM 0x10291966
616
617 strcpy (hname, host->h_name);
618
619 realm = krb_realmofhost (hname);
620
621 ret = krb_mk_auth (KOPT_DO_MUTUAL, &k.tkt,
622 GNATS_KRB4_PRINCIPAL_NAME, hname, realm,
623 CKSUM, GNATS_KRB4_VERSIONID, &k.packet);
624 remember_krb_auth_ret = ret;
625 if (ret)
626 SKIP (krb_get_err_text (ret));
627
628 ret = krb_get_cred (GNATS_KRB4_PRINCIPAL_NAME, hname, realm, &k.cred);
629 /* This should always work unless the ticket file has been changed
630 out from under the client at just the wrong time. */
631 if (ret)
632 abort ();
633
634 /* Since we don't currently have an interface for specifying a
635 "local name" on the server distinct from the Kerberos principal
636 name, just use the principal. */
637 me = k.cred.pname;
638
639 if (debug)
640 {
641 fprintf (stderr, "%s: writing `AUTH krb4-simple'\n", program_name);
642 }
643 fprintf (serv_write, "AUTH krb4-simple\r\n");
644 fflush (serv_write);
645 r = server_reply ();
646 if (!r || r->state == CODE_AUTH_TYPE_UNSUP)
647 {
648 SKIP ("not supported by server");
649 }
650 else if (r->state == CODE_ERROR)
651 {
652 fprintf (stderr, "%s: server authentication error: %s\n",
653 program_name, r->text);
654 goto krb_exit;
655 }
656 else if (r->state != CODE_OK)
657 {
658 abort ();
659 }
660 else
661 {
662 free_reply (r);
663 }
664
665 if (getsockname (sockfd, (struct sockaddr *) &laddr, &laddrlen) < 0)
666 {
667 fprintf (stderr, "%s: getsockname: %s", program_name,
668 strerror (errno));
669 exit (1);
670 }
671
672 k.buf[0] = 'x';
673 /* This would be better with a base-64 encoding, but I'm too lazy
674 to write it write now. */
675 tohex (k.packet.dat, k.buf + 1, k.packet.length);
676
677 fprintf (serv_write, "%s\r\n%s\r\n", me, k.buf);
678 fflush (serv_write);
679
680 i = fgetc (serv_read);
681 if (i == 'x')
682 /* ok */
683 k.buf[0] = i;
684 else
685 {
686 /* error return */
687 ungetc (i, serv_read);
688 r = server_reply ();
689 fprintf (stderr, "%s: %s\n", program_name, r->text);
690 goto krb_exit;
691 }
692 p = fgets (k.buf + 1, sizeof (k.buf) - 1, serv_read);
693 if (!p)
694 /* eof */
695 goto krb_exit;
696 p = strchr (k.buf + 1, '\r');
697 if (p)
698 *p = 0;
699 k.packet.length = (strlen (k.buf) - 1) / 2;
700 fromhex (k.packet.dat, k.buf + 1, k.packet.length);
701 ret = krb_check_auth (&k.packet, CKSUM, &k.msg_data,
702 k.cred.session, k.sched, &laddr, &server);
703 if (ret)
704 fprintf (stderr, "%s: authentication failed: %s\n",
705 program_name, krb_get_err_text (ret));
706
707 /* Now that authentication has succeeded (presumably), get an "ok"
708 response if authorization succeeds, or an error if it fails.
709 (Not everyone listed in the Kerberos database is necessarily
710 permitted to retrieve information from GNATS.) */
711 get_reply (stdout);
712
713 krb_exit:
714 /* Zero out the Kerberos scratch area, since it includes
715 authentication information we don't want going into core files
716 etc. */
717 memset ((char *) &k, '?', sizeof (k));
718 }
719 #endif
720 return 0;
721 }
722
723 void
client_exit(void)724 client_exit (void)
725 {
726 /* Do nothing, if we're not a network client. */
727 if (serv_read != NULL && serv_write != NULL)
728 {
729 /* That's it, say bye-bye. */
730 if (debug)
731 {
732 fprintf (stderr, "%s: writing `QUIT'\n", program_name);
733 }
734 fflush (serv_read);
735 fprintf (serv_write, "QUIT\r\n");
736
737 fclose (serv_write);
738 fclose (serv_read);
739 serv_write = NULL;
740 serv_read = NULL;
741 }
742 if (forkedGnatsd != 0)
743 {
744 int status;
745
746 wait (&status);
747 }
748 }
749
750
751 /* change database command to recognize databse aliases for network
752 client commands like nquery-pr. */
753 void
client_chdb(const char * newRoot)754 client_chdb (const char *newRoot)
755 {
756 if (newRoot == NULL)
757 {
758 newRoot = "default";
759 }
760
761 /* send the change db command and get server's reply */
762 if (debug)
763 {
764 fprintf (stderr, "%s: writing `CHDB %s'\n", program_name, newRoot);
765 }
766 fprintf (serv_write, "CHDB %s\r\n", newRoot);
767 get_reply (stdout); /* this will exit on error */
768 }
769
770 static void
client_user(const char * user,const char * passwd)771 client_user (const char *user, const char *passwd)
772 {
773 /* send the user command and get server's reply */
774 if (debug)
775 {
776 fprintf (stderr, "%s: writing `USER %s %s'\n", program_name,
777 user, passwd);
778 }
779 fprintf (serv_write, "USER %s %s\r\n", user, passwd);
780 get_reply(stdout); /* this will exit on error */
781 }
782
783 /* Scan the environment and the global database configuration file for the
784 info needed to connect to the gnatsd server. */
785 void
scanEnv(char ** user,char ** passwd,char ** hostname,int * port,char ** database)786 scanEnv (char **user, char **passwd, char **hostname,
787 int *port, char **database)
788 {
789 char *vals[5];
790 char *evar = *database == NULL ? getenv ("GNATSDB") : *database;
791 bool is_net_conn = databaseSpecIsNetConn (evar);
792
793 if (evar == NULL || evar[0] == '\0' || is_net_conn)
794 {
795 if (*hostname == NULL)
796 {
797 const char *specS = databaseSpecServer (evar);
798 if (specS != NULL)
799 {
800 *hostname = xstrdup (specS);
801 }
802 }
803 if (*port == -1)
804 {
805 const char *pname = databaseSpecPort (evar);
806
807 if (pname != NULL)
808 {
809 if (isdigit ((int) (pname[0])))
810 {
811 *port = atoi (pname);
812 }
813 else
814 {
815 struct servent *s = getservbyname (pname, "tcp");
816 if (s != NULL)
817 {
818 *port = ntohs (s->s_port);
819 }
820 }
821 }
822 }
823 }
824 else
825 {
826 char *place = evar;
827 int x;
828
829 for (x = 0; x < 5; x++)
830 {
831 if (place != NULL)
832 {
833 char *nplace = strchr (place, ':');
834 if (nplace != NULL)
835 {
836 vals[x] = xmalloc (nplace - place + 1);
837 memcpy (vals[x], place, nplace - place);
838 vals[x][nplace - place] = '\0';
839 place = nplace + 1;
840 }
841 else
842 {
843 vals[x] = xstrdup (place);
844 place = NULL;
845 }
846 }
847 else
848 {
849 vals[x] = xstrdup ("");
850 }
851 }
852
853 if (*hostname == NULL)
854 {
855 *hostname = vals[0];
856 }
857 else
858 {
859 free (vals[0]);
860 }
861
862 if (*port == -1 && vals[1][0] != '\0')
863 {
864 *port = atoi (vals[1]);
865 }
866
867 free (vals[1]);
868 if (*database == NULL && vals[2][0] != '\0')
869 {
870 *database = vals[2];
871 }
872 else
873 {
874 free (vals[2]);
875 }
876
877 if (*user == NULL && vals[3][0] != '\0')
878 {
879 *user = vals[3];
880 }
881 else
882 {
883 free (vals[3]);
884 }
885
886 if (*passwd == NULL && vals[4][0] != '\0')
887 {
888 *passwd = vals[4];
889 }
890 else
891 {
892 free (vals[4]);
893 }
894 }
895
896 if (*user == NULL)
897 {
898 /* This is just wrong, but we'll live with it. XXX ??? !!! */
899 char *lname = getlogin ();
900 if (lname != NULL)
901 {
902 *user = xstrdup (lname);
903 }
904 else
905 {
906 struct passwd *p;
907
908 p = getpwuid (getuid ());
909 if (p != NULL)
910 {
911 *user = xstrdup (p->pw_name);
912 }
913 }
914 }
915
916 if (*passwd == NULL)
917 {
918 *passwd = xstrdup ("*");
919 }
920
921 if (*database == NULL && ! is_net_conn)
922 {
923 *database = ((evar == NULL || evar[0] == '\0')
924 ? xstrdup ("default")
925 : xstrdup (evar));
926 }
927
928 if (*port == -1)
929 {
930 struct servent *s = getservbyname (DEFAULT_GNATS_SERVICE, "tcp");
931 if (s != NULL)
932 {
933 *port = ntohs (s->s_port);
934 }
935 }
936 }
937
938 int
client_init_gnats(ErrorDesc * err,const char * userArg,const char * passwdArg,const char * hostnameArg,int port,const char * databaseArg)939 client_init_gnats (ErrorDesc *err, const char *userArg, const char *passwdArg,
940 const char *hostnameArg, int port, const char *databaseArg)
941 {
942 char *user = NULL;
943 char *passwd = NULL;
944 char *hostname = NULL;
945 char *database = NULL;
946 int res = -1;
947
948 if (userArg != NULL)
949 {
950 user = xstrdup (userArg);
951 }
952
953 if (passwdArg != NULL)
954 {
955 passwd = xstrdup (passwdArg);
956 }
957
958 if (hostnameArg != NULL)
959 {
960 hostname = xstrdup (hostnameArg);
961 }
962
963 if (databaseArg != NULL)
964 {
965 database = xstrdup (databaseArg);
966 }
967
968 scanEnv (&user, &passwd, &hostname, &port, &database);
969
970 if (hostname == NULL)
971 {
972 setError (err, CODE_INVALID_DATABASE,
973 "Can't determine hostname to connect to");
974 }
975 else if (port < 0)
976 {
977 setError (err, CODE_INVALID_DATABASE,
978 "Can't determine port number to connect to");
979 }
980 else if (user == NULL)
981 {
982 setError (err, CODE_INVALID_DATABASE,
983 "Can't determine username to send to the server");
984 }
985 else if (passwd == NULL)
986 {
987 setError (err, CODE_INVALID_DATABASE,
988 "Can't determine password to send to the server");
989 }
990 else if (clientConnect (err, hostname, port) == 0)
991 {
992 client_chdb (database);
993 client_user (user, passwd);
994 res = 0;
995 }
996
997 if (user != NULL)
998 {
999 free (user);
1000 }
1001 if (passwd != NULL)
1002 {
1003 free (passwd);
1004 }
1005 if (hostname != NULL)
1006 {
1007 free (hostname);
1008 }
1009 if (database != NULL)
1010 {
1011 free (database);
1012 }
1013
1014 return res;
1015 }
1016
1017 static DatabaseInfo lockedDatabase = NULL;
1018
1019 int
client_lock_gnats(const DatabaseInfo database,ErrorDesc * err)1020 client_lock_gnats (const DatabaseInfo database, ErrorDesc *err)
1021 {
1022 if (lockedDatabase != NULL)
1023 {
1024 abort ();
1025 }
1026 lockedDatabase = database;
1027 return lock_gnats (database, err);
1028 }
1029
1030 void
client_unlock_gnats(void)1031 client_unlock_gnats (void)
1032 {
1033 ErrorDesc err;
1034 DatabaseInfo database = lockedDatabase;
1035 lockedDatabase = NULL;
1036
1037 if (database != NULL)
1038 {
1039 if (unlock_gnats (database, &err) < 0)
1040 {
1041 client_print_errors (database, err);
1042 }
1043 }
1044 }
1045
1046 void
client_print_errors(const DatabaseInfo database,ErrorDesc err)1047 client_print_errors (const DatabaseInfo database, ErrorDesc err)
1048 {
1049 switch (getErrorCode (err))
1050 {
1051 case CODE_NO_INDEX:
1052 case CODE_FILE_ERROR:
1053 fprintf (stderr, "%s: %s\n", program_name, getErrorMessage (err));
1054 punt (database, 1, getErrorMessage (err));
1055 break;
1056
1057 case CODE_INVALID_FIELD_CONTENTS:
1058 {
1059 BadFields badFieldList = getBadFieldList (err);
1060 while (badFieldList != NULL)
1061 {
1062 fprintf (stderr, "%s: invalid value `%s' in field %s\n",
1063 program_name,
1064 badFieldValue (badFieldList),
1065 fieldDefForIndex (badFieldIndex (badFieldList))->name);
1066 badFieldList = nextBadField (badFieldList);
1067 }
1068 }
1069 break;
1070
1071 case CODE_INVALID_ENUM:
1072 printf ("%s: invalid fields:\n", program_name);
1073 {
1074 BadFields badFieldList = getBadFieldList (err);
1075 while (badFieldList != NULL)
1076 {
1077 FieldIndex f = badFieldIndex (badFieldList);
1078 printf ("\tNote: There was a bad value `%s' for the field `%s'.\n\tIt was set to the default value of `%s'.\n",
1079 badFieldValue (badFieldList),
1080 fieldDefForIndex (f)->name,
1081 fieldDefForIndex (f)->default_value);
1082 badFieldList = nextBadField (badFieldList);
1083 }
1084 }
1085 break;
1086
1087 default:
1088 fprintf (stderr, "%s: %s\n", program_name, getErrorMessage (err));
1089 break;
1090 }
1091 }
1092
1093 void
sendRemoteListQuery(ListTypes whichList,FILE * outfile)1094 sendRemoteListQuery (ListTypes whichList, FILE *outfile)
1095 {
1096 const char *listName = listTypeToString (whichList);
1097 if (listName != NULL)
1098 {
1099 if (debug)
1100 {
1101 fprintf (stderr, "%s: writing `LIST %s'\n",
1102 program_name, listName);
1103 }
1104 fprintf (serv_write, "LIST %s\r\n", listName);
1105 if (outfile != NULL)
1106 {
1107 get_reply (outfile);
1108 }
1109 }
1110 }
1111
1112 static void
sendQueryFormat(const char * name)1113 sendQueryFormat (const char *name)
1114 {
1115 if (debug)
1116 {
1117 fprintf (stderr, "%s: writing `QFMT %s'\n", program_name, name);
1118 }
1119 fprintf (serv_write, "QFMT %s\r\n", name);
1120 get_reply (NULL);
1121 }
1122
1123 void
sendRemoteQuery(const char * queryString,const char ** args,const char * query_format_name,FILE * outfile)1124 sendRemoteQuery (const char *queryString, const char **args,
1125 const char *query_format_name, FILE *outfile)
1126 {
1127 char *buf;
1128 int i = 0;
1129 size_t buflen;
1130
1131 if (queryString != NULL && queryString[0] != '\0')
1132 {
1133 if (debug)
1134 {
1135 fprintf (stderr, "%s: writing `EXPR %s'\n", program_name,
1136 queryString);
1137 }
1138 fprintf (serv_write, "EXPR %s\r\n", queryString);
1139 get_reply (NULL);
1140 }
1141
1142 sendQueryFormat (query_format_name);
1143
1144 /* I'm not entirely sure why we go through this foolishness of
1145 building up a string containing the command, only to just
1146 print it out--why not call fprintf() a bunch of times? It'd
1147 probably be faster. XXX ??? !!! */
1148 buf = xstrdup ("QUER");
1149 buflen = 4;
1150
1151 while (args != NULL && args[i] != NULL)
1152 {
1153 size_t arglen = strlen (args[i]);
1154
1155 buf = xrealloc (buf, buflen + arglen + 2);
1156 buf[buflen] = ' ';
1157 memcpy (buf + buflen + 1, args[i], arglen + 1);
1158 buflen += arglen + 1;
1159 i++;
1160 }
1161
1162 if (debug)
1163 {
1164 fprintf (stderr, "%s: writing `%s'\n", program_name, buf);
1165 }
1166 fprintf (serv_write, "%s\r\n", buf);
1167 free (buf);
1168
1169 if (outfile != NULL)
1170 {
1171 get_reply (outfile);
1172 }
1173 }
1174
1175 void
clientGetAdmField(FILE * outfile,const char * admField,const char * admSubfield,const char * admKey)1176 clientGetAdmField (FILE *outfile, const char *admField,
1177 const char *admSubfield, const char *admKey)
1178 {
1179 if (admSubfield == NULL)
1180 {
1181 admSubfield = "";
1182 }
1183 fprintf (serv_write, "ADMV %s %s %s\r\n", admField, admKey, admSubfield);
1184 get_reply (outfile);
1185 }
1186
1187 /* Read PR PRNUM from the GNATS server, and return it. */
1188 PR *
clientReadPR(const char * prNum)1189 clientReadPR (const char *prNum)
1190 {
1191 PR *res;
1192
1193 prBeingRead = allocPR (NULL);
1194 fprintf (serv_write, "RSET\r\n");
1195 get_reply (NULL);
1196 fprintf (serv_write, "QFMT full\r\n");
1197 get_reply (NULL);
1198 fprintf (serv_write, "QUER %s\r\n", prNum);
1199 initReadPR (prBeingRead);
1200 get_reply (NULL);
1201 res = prBeingRead;
1202 prBeingRead = NULL;
1203 return res;
1204 }
1205
1206 /* Return a list of PRs that match the specified query expression. */
1207 StringList *
clientGetPRList(const char * expr)1208 clientGetPRList (const char *expr)
1209 {
1210 StringList *res = NULL;
1211
1212 fprintf (serv_write, "RSET\r\n");
1213 get_reply (NULL);
1214 sendRemoteQuery (expr, NULL, "Number", NULL);
1215 prList = &res;
1216 get_reply (NULL);
1217 return res;
1218 }
1219
1220 void
netSetEditEmailAddr(const char * addr)1221 netSetEditEmailAddr (const char *addr)
1222 {
1223 fprintf (serv_write, "EDITADDR %s\r\n", addr);
1224 get_reply (NULL);
1225 }
1226
1227 static void
netSendPRCmd(const char * cmd,FILE * file,int show_information)1228 netSendPRCmd (const char *cmd, FILE *file, int show_information)
1229 {
1230 char *line;
1231 if (debug)
1232 {
1233 fprintf (stderr, "%s: writing `%s'\n", program_name, cmd);
1234 }
1235 fprintf (serv_write, "%s\r\n", cmd);
1236 get_reply (stdout); /* XXX */
1237
1238 while ((line = read_line (file, NULL)) != NULL)
1239 {
1240 write_multitext (serv_write, line, "\r\n");
1241 free (line);
1242 }
1243 fprintf (serv_write, ".\r\n");
1244 /* if get_reply finds errors it writes messages and doesn't return */
1245 get_reply (show_information ? stdout : NULL);
1246 }
1247
1248 void
netCheckPR(FILE * file,int initial)1249 netCheckPR (FILE *file, int initial)
1250 {
1251 netSendPRCmd (initial ? "CHEK INIT" : "CHEK", file, 1);
1252 }
1253
1254 void
netEditField(FILE * fp,const char * prnum,const char * fieldname,const char * editEmailAddr,int append,char * reason)1255 netEditField (FILE *fp, const char *prnum, const char *fieldname,
1256 const char *editEmailAddr, int append, char *reason)
1257 {
1258 const char *cmd;
1259 char *line;
1260 int reply;
1261
1262 netSetEditEmailAddr (editEmailAddr);
1263 if (append)
1264 {
1265 cmd = "APPN";
1266 }
1267 else
1268 {
1269 cmd = "REPL";
1270 }
1271 fprintf (serv_write, "%s %s %s\r\n", cmd, prnum, fieldname);
1272
1273 do {
1274 reply = get_reply (stdout);
1275 switch (reply)
1276 {
1277 case CODE_SEND_TEXT:
1278 while ((line = read_line (fp, NULL)) != NULL)
1279 {
1280 write_multitext (serv_write, line, "\r\n");
1281 free (line);
1282 }
1283 fprintf (serv_write, ".\r\n");
1284 break;
1285
1286 case CODE_SEND_CHANGE_REASON:
1287 if (reason)
1288 fprintf (serv_write, "%s\r\n.\r\n", reason);
1289 else
1290 {
1291 fprintf (stderr, "%s: server wants a change-reason for %s,"
1292 " but none was supplied.\n", program_name, fieldname);
1293 safe_exit ();
1294 }
1295 break;
1296
1297 case CODE_OK:
1298 break;
1299
1300 default:
1301 fprintf (stderr, "%s: bad reply from server: %d.\n",
1302 program_name, reply);
1303 safe_exit ();
1304 break;
1305 }
1306 } while (reply != CODE_OK);
1307 }
1308
1309 void
netSubmitNewPR(FILE * file,int show_prnum)1310 netSubmitNewPR (FILE *file, int show_prnum)
1311 {
1312 netSendPRCmd ("SUBM", file, show_prnum);
1313 }
1314
1315 void
netModifyPR(FILE * file,const char * prNum,const char * editEmailAddr)1316 netModifyPR (FILE *file, const char *prNum, const char *editEmailAddr)
1317 {
1318 char *buf;
1319
1320 netSetEditEmailAddr (editEmailAddr);
1321 asprintf (&buf, "EDIT %s", prNum);
1322 netSendPRCmd (buf, file, 1);
1323 free (buf);
1324 }
1325
1326 void
netLockDB(void)1327 netLockDB (void)
1328 {
1329 if (debug)
1330 {
1331 fprintf (stderr, "%s: writing `LKDB'\n", program_name);
1332 }
1333 fprintf (serv_write, "LKDB\r\n");
1334 get_reply (stdout);
1335 }
1336
1337 void
netUnlockDB(void)1338 netUnlockDB (void)
1339 {
1340 if (debug)
1341 {
1342 fprintf (stderr, "%s: writing `UNDB'\n", program_name);
1343 }
1344 fprintf (serv_write, "UNDB\r\n");
1345 get_reply (stdout);
1346 }
1347
1348 void
netLockPR(const char * prNum,const char * username,const char * processID)1349 netLockPR (const char *prNum, const char *username, const char *processID)
1350 {
1351 if (processID != NULL)
1352 {
1353 fprintf (serv_write, "LOCK %s %s %s\r\n", prNum, username, processID);
1354 }
1355 else
1356 {
1357 fprintf (serv_write, "LOCK %s %s\r\n", prNum, username);
1358 }
1359 get_reply (stdout);
1360 }
1361
1362 void
netUnlockPR(const char * prNum)1363 netUnlockPR (const char *prNum)
1364 {
1365 if (debug)
1366 {
1367 fprintf (stderr, "%s: writing `UNLK %s'\n", program_name, prNum);
1368 }
1369 fprintf (serv_write, "UNLK %s\r\n", prNum);
1370 get_reply (stdout);
1371 }
1372
1373 void
netDeletePR(const char * prNum,const char * editUserEmailAddr)1374 netDeletePR (const char *prNum, const char *editUserEmailAddr)
1375 {
1376 netSetEditEmailAddr (editUserEmailAddr);
1377 if (debug)
1378 {
1379 fprintf (stderr, "%s: writing `DELETE %s'\n", program_name, prNum);
1380 }
1381 fprintf (serv_write, "DELETE %s\r\n", prNum);
1382 get_reply (stdout);
1383 }
1384
1385 void
netFieldFlags(const char * fieldname)1386 netFieldFlags (const char *fieldname)
1387 {
1388 fprintf (serv_write, "FIELDFLAGS %s\r\n", fieldname);
1389 get_reply (stdout);
1390 }
1391
1392 void
netValidValues(const char * fieldname)1393 netValidValues (const char *fieldname)
1394 {
1395 fprintf (serv_write, "FVLD %s\r\n", fieldname);
1396 get_reply (stdout);
1397 }
1398
1399 void
netFieldDescription(const char * fieldname)1400 netFieldDescription (const char *fieldname)
1401 {
1402 fprintf (serv_write, "FDSC %s\r\n", fieldname);
1403 get_reply (stdout);
1404 }
1405
1406 void
netFieldType(const char * fieldname)1407 netFieldType (const char *fieldname)
1408 {
1409 fprintf (serv_write, "FTYP %s\r\n", fieldname);
1410 get_reply (stdout);
1411 }
1412