summaryrefslogtreecommitdiffstats
path: root/ax25/axgetput/axgetput.c
blob: 55e83641987232adbc0dded4776fd5af72d12891 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
static const char rcsid[] = "@(#) $Id: axgetput.c,v 1.5 2009/06/23 22:13:51 ralf Exp $";

/*
 * This is axgetput
 *
 * This shell utility is for up/downloading files via your ax25 unix login
 * session which is managed by axspawn.
 * you need a //BIN compatible axspawn, which has the functionality of making
 * the stream 8bit compatible (normally the EOL-conversion <LF> <-> <CR>
 * prevents this). see:
 *   http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/patches/linux-ax25/
 *
 * (c) 2002 Thomas Osterried  DL9SAU <thomas@x-berg.in-berlin.de>
 * License: GPL. See http://www.fsf.org/
 * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/
 *
 */


#include "includes.h"

#include "axgetput.h"
#include "util.h"
#include "proto_bin.h"

int fdin = 0;
int fdout = 1;
int fderr = 2;
int fdout_is_pipe = 0;
int fdin_is_pipe = 0;

int is_stream = 0;
int mode = 0;
int do_crc_only = 0;

char c_eol = '\n';

unsigned int BLOCKSIZ = BLOCKSIZ_DEFAULT;

char *send_on_signal = 0;

static struct termios prev_termios;
static int prev_termios_stored = 0;
static mode_t mode_tty = 0;

#ifndef	MYNAME
#define MYNAME "axgetput"
#endif

struct revision {
  char number[16];
  char date[16];
  char time[16];
  char author[16];
  char state[16];
};

static struct revision revision;

/*---------------------------------------------------------------------------*/

static void set_tty_flags(void)
{
  struct termios termios;
  struct stat statbuf;

  if (fdin_is_pipe)
    return;
  /* mesg no  */
  if (!fstat(fdin, &statbuf)) {
    /* save old mode  */
    mode_tty = statbuf.st_mode;
    fchmod(fdin, 0600);
  }
  /* make tty 8bit clean  */
  if (tcgetattr(fdin, &prev_termios) != -1)
    prev_termios_stored = 1;
  memset((char *) &termios, 0, sizeof(termios));
  termios.c_iflag = IGNBRK | IGNPAR;
  termios.c_oflag = 0;
  termios.c_cflag = CBAUD | CS8 | CREAD | CLOCAL;
  termios.c_cflag = ~(CSTOPB|PARENB|PARODD|HUPCL);
  termios.c_lflag = 0;
  termios.c_cc[VMIN] = 1;
  termios.c_cc[VTIME] = 0;
  termios.c_cc[VSTART] = -1;
  termios.c_cc[VSTOP] = -1;
  tcsetattr(fdin, TCSANOW, &termios);
}

/*---------------------------------------------------------------------------*/

static void restore_tty_flags(void)
{
  if (fdin_is_pipe)
    return;
  if (prev_termios_stored)
    tcsetattr(fdin, TCSANOW, &prev_termios);
  if (mode_tty)
    fchmod(fdin, mode_tty);
}

/*---------------------------------------------------------------------------*/

static void eol_convention(int state_should)
{
  /* need patched axspawn */
#define	BIN_ON	"//BIN ON\r"
#define	BIN_OFF	"//BIN OFF\r"
  static int state_is = 0;

  /* already in correct state?  */
  if ((state_is && state_should) || (!state_is && !state_should))
	return;

  sleep(1);

  if (state_should) {
    write(fderr, BIN_ON, strlen(BIN_ON));
    c_eol = '\r';
  } else {
    write(fderr, BIN_OFF, strlen(BIN_OFF));
    c_eol = '\n';
  }
  state_is = state_should;

  sleep(1);
}

/*---------------------------------------------------------------------------*/

static void restore_defaults(void)
{
  eol_convention(0);
  restore_tty_flags();
}

/*---------------------------------------------------------------------------*/

static void signal_handler(int sig)
{
  if (send_on_signal)
    secure_write(fdout, send_on_signal, strlen(send_on_signal));
  restore_defaults();
  if (*err_msg) {
    fputs(err_msg, stderr);
  }
  fprintf(stderr, "Died by signal %d.\n", sig);
  exit(sig);
}

/*---------------------------------------------------------------------------*/

void do_version(void)
{
        fprintf(stderr, MYNAME " %s %s\n", revision.number, revision.date);
	fprintf(stderr, "  (c) 2002 Thomas Osterried <thomas@x-berg.in-berlin.de>\n");
	fprintf(stderr, "  License: GPL. See http://www.fsf.org/\n");
	fprintf(stderr, "  Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/\n");
}

/*---------------------------------------------------------------------------*/

static void usage(int all) {
  fprintf(stderr, "usage: %s ", myname);
  if (mode % 2) {
    fprintf(stderr, "[-ivh] [filename]\n");
  } else {
    fprintf(stderr, "[-isvh] [-b <blocksize>] [filename]\n");
  }
  fprintf(stderr, "  -h prints detailed help\n");
  fprintf(stderr, "  -i computes checksum only\n");
  fprintf(stderr, "  -v prints version and exits\n");
  if (!all)
    return;
  if (mode % 2) {
    fprintf(stderr, "  filename is usually got from the remote side by the protocol\n");
    fprintf(stderr, "  but can be forced if you like to ignore this.\n");
    fprintf(stderr, "  filename should be ommitted if output is sent to a pipe\n.");
  } else {
    fprintf(stderr, "  -b value is the blocksize (framelen) of the transmitted data\n");
    fprintf(stderr, "     default %d, which is a useful choice for ampr ax25.\n", BLOCKSIZ_DEFAULT);
    fprintf(stderr, "  -s indicates a stream with unknown size.\n");
    fprintf(stderr, "     otherwise, the data will be read to memory until EOF.\n");
    fprintf(stderr, "  -s is only available if stdin is a pipe\n");
    fprintf(stderr, "  if filename specified in filter, the given name will be suggested instead.\n");
    fprintf(stderr, "  filename may be ommited if used as filter.\n");
  }
  fputc('\n', stderr);
  fprintf(stderr, "Tips: - compressed download:\n");
  fprintf(stderr, "        gzip -c foo.txt | bget foo.txt.gz\n");
  fprintf(stderr, "        tar cjf - ~/foo ~/bar/ | bget my_data.tar.bz2\n");
  fputc('\n', stderr);
  fprintf(stderr, "Other protocols:\n");
  fprintf(stderr, "  bget / bput: receive / send with #BIN# protocol\n");
  fprintf(stderr, "  yget / yput: receive / send with yapp\n");
  fprintf(stderr, "  rget / rput: receive / send with didadit\n");
  fprintf(stderr, "These are (sym)links to one program: " MYNAME "\n");
}


/*---------------------------------------------------------------------------*/

/* not implemented */
static int yput(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; }
static int yget(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; }
static int rget(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; }
static int rput(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; }

/*---------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{

  int len;
  int ret = 0;
  int c;
  char *p;
  char buf[1024];

  strcpy(buf, rcsid);
  sscanf(buf, "%*s %*s %*s %s %s %s %s %s",
    revision.number,
    revision.date,
    revision.time,
    revision.author,
    revision.state);

  /* determine what to so in the way how we are called  */
  if ((p = strrchr(argv[0], '/')))
    p++; /* skip '/'  */
  else
    p = argv[0];
  len = strlen(p);
  if (len < 0 || len > sizeof(myname)-1)
    len = sizeof(myname)-1;
  strncpy(myname, p, len);
  myname[len] = 0;
  strlwc(myname);

  fdin_is_pipe = (isatty(fdin) ? 0 : 1);
  fdout_is_pipe = (isatty(fdout) ? 0 : 1);

  if (fdin_is_pipe && fdout_is_pipe) { 
    fprintf(stderr, "error: cannot work in between two pipes\n");
    exit(1);
  }

  *filename = 0;
  *err_msg = 0;

  if (!strcmp(myname, "bput"))
    mode = RECV_BIN;
  else if (!strcmp(myname, "bget"))
    mode = SEND_BIN;
  else if (!strcmp(myname, "yput"))
    mode = RECV_YAPP;
  else if (!strcmp(myname, "yget"))
    mode = SEND_YAPP;
  else if (!strcmp(myname, "rput"))
    mode = RECV_DIDADIT;
  else if (!strcmp(myname, "rget"))
    mode = SEND_DIDADIT;

  if (mode % 2) {
    if (fdin_is_pipe) {
      fprintf(stderr, "error: error: stdin must be a tty\n");
      exit(1);
    }
  } else {
    if (fdout_is_pipe) {
      fprintf(stderr, "error: stdout must be a tty\n");
      exit(1);
    }
  }

  signal(SIGHUP, signal_handler);
  signal(SIGTERM, signal_handler);
  signal(SIGINT, signal_handler);

/* for difference betreen "bput -f file" and "bget file"  */
#define	get_filename(f) { \
      if (!strcmp(f, "-")) { \
	if (mode % 2) \
          fdin_is_pipe = 1; \
        else \
          fdout_is_pipe = 1; \
      } else { \
        strncpy(filename, f, sizeof(filename)-1); \
        filename[sizeof(filename)-1] = 0; \
	if (mode % 2) { \
	  if (fdin_is_pipe) \
	    fdin_is_pipe = 0; \
	} else { \
          if (fdout_is_pipe) \
	    fdout_is_pipe = 0; \
	} \
      } \
}

  while ((c = getopt(argc, argv, (mode % 2) ? "ivh?" : "b:isvh?")) != EOF) {
    switch(c) {
    case 'b':
      if (((BLOCKSIZ = (unsigned ) atoi(optarg)) < BLOCKSIZ_MIN) || BLOCKSIZ > BLOCKSIZ_MAX) {
	fprintf(stderr, "error: invalid blocksize: %d\n", BLOCKSIZ);
	fprintf(stderr, "       blocksize must be in the range %d <= x <= %d. a value\n", BLOCKSIZ_MIN, BLOCKSIZ_MAX);
	fprintf(stderr, "       of %d (default) is suggested, because it fits in an ax25 frame.\n", BLOCKSIZ_DEFAULT);
	exit(1);
      }
      break;
    case 'i':
      do_crc_only = 1;
      break;
    case 's':
      is_stream = 1;
      break;
    case 'v':
      do_version();
      exit(0);
      break;
    case 'h':
    case '?':
      usage((c == 'h'));
      exit(0);
      break;
    }
  }

  if (mode == 0) {
    usage(1);
    exit(0);
  }

  if (optind < argc) {
    get_filename(argv[optind]);
    optind++;
  }
  if (optind < argc) {
    usage(0);
    exit(1);
  }

  if (is_stream && !fdin_is_pipe) {
    fprintf(stderr, "error: -s is only available in a pipe\n");
    exit(1);
  }

  if (do_crc_only)
    goto skiped_crc_only_tag_1;

  if (mode % 2) {
    if (fdin_is_pipe) {
      fprintf(stderr, "error: error: stdin must be a tty.\n");
      exit(1);
    }
    if (fdout_is_pipe && *filename) {
      fprintf(stderr, "error: filename in a pipe does not make sense.\n");
      exit(1);
    }
  } else {
    if (fdout_is_pipe) {
      fprintf(stderr, "error: stdout must be a tty.\n");
      exit(1);
    }
    if (!fdin_is_pipe && !*filename) {
      fprintf(stderr, "error: no file to send.\n");
      exit(1);
    }
  }

  signal(SIGQUIT, signal_handler);

  set_tty_flags();
  eol_convention(1);

skiped_crc_only_tag_1:

  switch (mode) {
  case RECV_BIN:
    if (do_crc_only)
      ret = bget();
    else
      ret = bput();
    break;
  case SEND_BIN:
    ret = bget();
    break;
  case RECV_YAPP:
    ret = yput();
    break;
  case SEND_YAPP:
    ret = yget();
    break;
  case RECV_DIDADIT:
    ret = rput();
    break;
  case SEND_DIDADIT:
    ret = rget();
    break;
  }

  restore_defaults();
  if (*err_msg) {
    fputs(err_msg, stderr);
  }
  exit(ret);

  return 0;

}