示例#1
0
 void peer_connection::start_read_loop()
 {
    if( !_read_loop.valid() )
       _read_loop = fc::async( [this](){ read_loop(); } );
 }
static void *run_read_loop(void *unused)
{
	struct sched_param param = { .sched_priority = 98 };
	pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
	read_loop(&queue, &df, read_interval);
	return NULL;
}

const char *argp_program_version = "SlotRecorder Diagnostic Tool 0.0.1";
static const char doc[] = "Records WMP network card activity.";
static const char args_doc[] = "";

static const struct argp_option options[] = {
	{ "interval", 'i', "INTERVAL", 0, "Interval between reads in microseconds." },
	{ 0 }
};

struct arguments {
	int interval;
};

static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
	struct arguments *arguments = state->input;
	uint interval;

	switch (key) {
	case 'i':
		if (sscanf(arg, "%u", &interval) < 1) {
			argp_error(state, "Invalid value for argument 'interval'.");
		}
		arguments->interval = interval;
		break;

	default:
		return ARGP_ERR_UNKNOWN;
	}

	return 0;
}

static const struct argp argp = { options, parse_opt, args_doc, doc };

int main(int argc, char *argv[])
{
	/* Set signal handler for interrupt signal. */
	memset(&sigact, 0, sizeof(sigact));
	sigact.sa_sigaction = sigint_handler;
	sigact.sa_flags = SA_SIGINFO;
	sigaction(SIGINT, &sigact, NULL);

	/* Parse command line arguments. */
	struct arguments arguments;
	memset(&arguments, 0, sizeof(arguments));
	argp_parse(&argp, argc, argv, 0, 0, &arguments);
	read_interval = arguments.interval;

	queue_init(&queue, 1024);
	init_file(&df);

	pthread_t reader;
	pthread_create(&reader, NULL, run_read_loop, NULL);
	process_loop(&queue, &df);
	pthread_join(reader, NULL);

	queue_destroy(&queue);
	close_file(&df);

	return 0;
}
// here all is done
int main(int argc, char *argv[])
{
  char *ptr;
  int i;
  int nType = HW_PCI;
  __u32 dwPort = 0;
  __u16 wIrq = 0;
  __u16 wBTR0BTR1 = 0;
  int   nExtended = CAN_INIT_TYPE_ST;
  const char  *szDevNode = DEFAULT_NODE;
  bool bDevNodeGiven = false;
  bool bTypeGiven    = false;
  char txt[VERSIONSTRING_LEN];

  errno = 0;

  current_release = CURRENT_RELEASE;
  disclaimer("receivetest");

  init();

  // decode command line arguments
  for (i = 1; i < argc; i++)
  {
    char c;

    ptr = argv[i];

    while (*ptr == '-')
      ptr++;

    c = *ptr;
    ptr++;

    if (*ptr == '=')
      ptr++;

    switch(tolower(c))
    {
      case 'f':
        szDevNode = ptr;
        bDevNodeGiven = true;
        break;
      case 't':
        nType = getTypeOfInterface(ptr);
        if (!nType)
        {
          errno = EINVAL;
          printf("receivetest: unknown type of interface!\n");
          goto error;
        }
        bTypeGiven = true;
        break;
      case 'p':
        dwPort = strtoul(ptr, NULL, 16);
        break;
      case 'i':
        wIrq   = (__u16)strtoul(ptr, NULL, 10);
        break;
      case 'e':
        nExtended = CAN_INIT_TYPE_EX;
        break;
      case '?':
      case 'h':
        hlpMsg();
        goto error;
        break;
      case 'b':
        wBTR0BTR1 = (__u16)strtoul(ptr, NULL, 16);
        break;
      default:
        errno = EINVAL;
        perror("receivetest: unknown command line argument!\n");
        goto error;
        break;
    }
  }
  
  // simple command input check
  if (bDevNodeGiven && bTypeGiven)
  {
    errno = EINVAL;
    perror("receivetest: device node and type together is useless");
    goto error;
  }

  // give some information back
  if (!bTypeGiven)
  {
    printf("receivetest: device node=\"%s\"\n", szDevNode);
  }
  else
  {
    printf("receivetest: type=%s", getNameOfInterface(nType));
    if (nType == HW_USB)
    {
      if (dwPort)
        printf(", %d. device\n", dwPort);
      else
        printf(", standard device\n");
    }
    else{
      if (dwPort)
      {
        if (nType == HW_PCI)
          printf(", %d. PCI device", dwPort);
        else
          printf(", port=0x%08x", dwPort);
      }
      else
        printf(", port=default");
      if ((wIrq) && !(nType == HW_PCI))
        printf(" irq=0x%04x\n", wIrq);
      else
        printf(", irq=default\n");
    }
  }

  if (nExtended == CAN_INIT_TYPE_EX)
    printf("             Extended frames are accepted");
  else
    printf("             Only standard frames are accepted");
  if (wBTR0BTR1)
    printf(", init with BTR0BTR1=0x%04x\n", wBTR0BTR1);
  else
    printf(", init with 500 kbit/sec.\n");

  /* open CAN port */
  if ((bDevNodeGiven) || (!bDevNodeGiven && !bTypeGiven)) 
  {
    h = LINUX_CAN_Open(szDevNode, O_RDWR);
    if (!h)
    {
      printf("receivetest: can't open %s\n", szDevNode);
      goto error;
    }
  }
  else 
  {
    // please use what is appropriate  
    // HW_DONGLE_SJA 
    // HW_DONGLE_SJA_EPP 
    // HW_ISA_SJA 
    // HW_PCI 
    h = CAN_Open(nType, dwPort, wIrq);
    if (!h)
    {
      printf("receivetest: can't open %s device.\n", getNameOfInterface(nType));
      goto error;
    }
  }

  /* clear status */
  //  CAN_Status(h);

 // get version info
  errno = CAN_VersionInfo(h, txt);
  if (!errno)
    printf("receivetest: driver version = %s\n", txt);
  else {
    perror("receivetest: CAN_VersionInfo()");
    goto error;
  }

  // init to a user defined bit rate
  if (wBTR0BTR1)
  {
    errno = CAN_Init(h, wBTR0BTR1, nExtended);
    if (errno) 
    {
      perror("receivetest: CAN_Init()");
      goto error;
    }
  }
  errno = read_loop();
  if (!errno)
    return 0;

  error:
    do_exit(errno);
    return errno;
}
示例#4
0
文件: main.cpp 项目: ytakano/nfwall
int
main(int argc, char *argv[])
{
        int n;
        int begin, end;
        int port;
        nat_type     type;
        time_t       ttl;


        int          ch;
        extern char *optarg;
        extern int   optind;

        progname = argv[0];

        begin = 0;
        end   = 65535;
        ttl   = 300;
        type  = full_cone;
        port  = 0;

        while ((ch = getopt(argc, argv, "p:b:e:t:n:v")) != -1){
                switch (ch){
                case 'p':
                        port = atoi(optarg);
                        break;
                case 'b':
                        begin = atoi(optarg);
                        break;
                case 'e':
                        end = atoi(optarg);
                        break;
                case 't':
                        ttl = atoi(optarg);
                        break;
                case 'n':
                        n = atoi(optarg);
                        if (n == 0) {
                                type = full_cone;
                        } else if (n == 1) {
                                type = restricted_cone;
                        } else if (n == 2) {
                                type = port_restricted_cone;
                        }
                        break;
                case 'v':
                        verbose = true;
                        break;
                default:
                        usage();
                        return -1;
                }
        }
        argc -= optind;
        argv += optind;


        if (port == 0) {
                std::cerr << "please use -p option for divert socket"
                          << std::endl;
                usage();
                return -1;
        }


        filter f(begin, end, type, ttl);
        int fd;

        fd = open_divert(port);
        if (fd < 0) {
                return -1;
        }

        read_loop(fd, f);

        return 0;
}
/* main thread of execution */
int main(int argc, char* args[])
{
    int fd;
    char* index, *db, *stream;
    FILE* indexf;
    struct bitarray* bits;

    fprintf_blue(stdout, "gammaray Async Queuer -- "
                         "By: Wolfgang Richter "
                         "<*****@*****.**>\n");
    redis_print_version();

    if (argc < 4)
    {
        fprintf_light_red(stderr, "Usage: %s <index file> <stream file>"
                                  " <redis db num>\n", args[0]);
        return EXIT_FAILURE;
    }

    index = args[1];
    stream = args[2];
    db = args[3];

    /* ----------------- hiredis ----------------- */
    struct kv_store* handle = redis_init(db, true);
    if (handle == NULL)
    {
        fprintf_light_red(stderr, "Failed getting Redis context "
                                  "(connection failure?).\n");
        return EXIT_FAILURE;
    }

    fprintf_cyan(stdout, "Loading MD filter from: %s\n\n", index);
    indexf = fopen(index, "r");

    if (indexf == NULL)
    {
        fprintf_light_red(stderr, "Error opening index file to get MD "
                                  "filter.\n");
        return EXIT_FAILURE;
    }

    if (qemu_load_md_filter(indexf, &bits))
    {
        fprintf_light_red(stderr, "Error getting MD filter from BSON file.\n");
        bits = bitarray_init(5242880);
        bitarray_set_all(bits);
    }

    if (bits == NULL)
    {
        fprintf_light_red(stderr, "Bitarray is NULL!\n");
        return EXIT_FAILURE;
    }

    fclose(indexf);

    fprintf_cyan(stdout, "Attaching to stream: %s\n\n", stream);

    on_exit((void (*) (int, void *)) redis_shutdown, handle);

    if (strcmp(stream, "-") != 0)
    {
        fd = open(stream, O_RDONLY);
    }
    else
    {
        fd = STDIN_FILENO;
    }
    
    if (fd == -1)
    {
        fprintf_light_red(stderr, "Error opening stream file. "
                                  "Does it exist?\n");
        return EXIT_FAILURE;
    }

    read_loop(fd, handle, bits);
    close(fd);
    return EXIT_SUCCESS;
}
char *passwdqc_random(const passwdqc_params_qc_t *params)
{
    char output[0x100], *retval;
    int bits;
    int word_count, trailing_separator, use_separators, toggle_case;
    int i;
    unsigned int max_length, length, extra;
    const char *start, *end;
    int fd;
    unsigned char bytes[3];

    bits = params->random_bits;
    if (bits < BITS_MIN || bits > BITS_MAX)
        return NULL;

    /*
     * Calculate the number of words to use.  The first word is always present
     * (hence the "1 +" and the "- WORD_BITS").  Each one of the following words,
     * if any, is prefixed by a separator character, so we use SWORD_BITS when
     * calculating how many additional words to use.  We divide "bits - WORD_BITS"
     * by SWORD_BITS with rounding up (hence the addition of "SWORD_BITS - 1").
     */
    word_count = 1 + (bits + (SWORD_BITS - 1 - WORD_BITS)) / SWORD_BITS;

    /*
     * Special case: would we still encode enough bits if we omit the final word,
     * but keep the would-be-trailing separator?
     */
    trailing_separator = (SWORD_BITS * (word_count - 1) >= bits);
    word_count -= trailing_separator;

    /*
     * To determine whether we need to use different separator characters or maybe
     * not, calculate the number of words we'd need to use if we don't use
     * different separators.  We calculate it by dividing "bits" by WORD_BITS with
     * rounding up (hence the addition of "WORD_BITS - 1").  The resulting number
     * is either the same as or greater than word_count.  Use different separators
     * only if their use, in the word_count calculation above, has helped reduce
     * word_count.
     */
    use_separators = ((bits + (WORD_BITS - 1)) / WORD_BITS != word_count);
    trailing_separator &= use_separators;

    /*
     * Toggle case of the first character of each word only if we wouldn't achieve
     * sufficient entropy otherwise.
     */
    toggle_case = (bits >
                   ((WORD_BITS - 1) * word_count) +
                   (use_separators ?
                    (SEPARATOR_BITS * (word_count - !trailing_separator)) : 0));

    /*
     * Calculate and check the maximum possible length of a "passphrase" we may
     * generate for a given word_count.  We add 1 to WORDSET_4K_LENGTH_MAX to
     * account for separators (whether different or not).  When there's no
     * trailing separator, we subtract 1.  The check against sizeof(output) uses
     * ">=" to account for NUL termination.
     */
    max_length = word_count * (WORDSET_4K_LENGTH_MAX + 1) -
                 !trailing_separator;
    if (max_length >= sizeof(output) || (int)max_length > params->max)
        return NULL;

    if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
        return NULL;

    retval = NULL;
    length = 0;
    do {
        if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes))
            goto out;

        /*
         * Append a word.  Treating bytes as little-endian, we use bits 0 to 11 for the
         * word index, and bit 13 for toggling the case of the first character.  Bits
         * 12, 14, and 15 are left unused.  Bits 16 to 23 are left for the separator.
         */
        i = (((int)bytes[1] & 0x0f) << 8) | (int)bytes[0];
        start = _passwdqc_wordset_4k[i];
        end = memchr(start, '\0', WORDSET_4K_LENGTH_MAX);
        if (!end)
            end = start + WORDSET_4K_LENGTH_MAX;
        extra = end - start;
        /* The ">=" leaves room for either one more separator or NUL */
        if (length + extra >= sizeof(output))
            goto out;
        memcpy(&output[length], start, extra);
        if (toggle_case) {
            /* Toggle case if bit set (we assume ASCII) */
            output[length] ^= bytes[1] & 0x20;
            bits--;
        }
        length += extra;
        bits -= WORD_BITS - 1;

        if (bits <= 0)
            break;

        /*
         * Append a separator character.  We use bits 16 to 19.  Bits 20 to 23 are left
         * unused.
         *
         * Special case: we may happen to leave a trailing separator if it provides
         * enough bits on its own.  With WORD_BITS 13 and SEPARATOR_BITS 4, this
         * happens e.g. for bits values from 31 to 34, 48 to 51, 65 to 68.
         */
        if (use_separators) {
            i = bytes[2] & 0x0f;
            output[length++] = SEPARATORS[i];
            bits -= SEPARATOR_BITS;
        } else
            output[length++] = SEPARATORS[0];
    } while (bits > 0);

    /*
     * Since we may have added a separator after the check in the loop above, we
     * must check again now.
     */
    if (length < sizeof(output)) {
        output[length] = '\0';
        retval = strdup(output);
    }

out:
    memset(bytes, 0, sizeof(bytes));
    memset(output, 0, length);

    close(fd);

    return retval;
}
示例#7
0
文件: ntp.cpp 项目: BitMoneta/fc
 void start_read_loop()
 {
   _read_loop_done = _ntp_thread.async( [this](){ read_loop(); }, "ntp_read_loop" );
 }
示例#8
0
int main(int argc, char **argv)
{
	int opt;
	int i2c_bus = DEFAULT_I2C_BUS;
	int sample_rate = DEFAULT_SAMPLE_RATE_HZ;
	
	mag_mode = -1;

	memset(calFile, 0, sizeof(calFile));

	while ((opt = getopt(argc, argv, "b:s:y:amh")) != -1) {
		switch (opt) {
		case 'b':
			i2c_bus = strtoul(optarg, NULL, 0);
			
			if (errno == EINVAL)
				usage(argv[0]);
			
			if (i2c_bus < MIN_I2C_BUS || i2c_bus > MAX_I2C_BUS)
				usage(argv[0]);

			break;
		
		case 's':
			sample_rate = strtoul(optarg, NULL, 0);
			
			if (errno == EINVAL)
				usage(argv[0]);
			
			if (sample_rate < MIN_SAMPLE_RATE || sample_rate > MAX_SAMPLE_RATE)
				usage(argv[0]);

			break;

		case 'y':
			if (strlen(optarg) >= sizeof(calFile)) {
				printf("Choose a shorter path for the cal file\n");
				usage(argv[0]);
			}

			strcpy(calFile, optarg);
			break;

		case 'a':
			if (mag_mode != -1)
				usage(argv[0]);

			mag_mode = 0;
			break;

		case 'm':
			if (mag_mode != -1)
				usage(argv[0]);

			mag_mode = 1;
			break;
		
		case 'h':
		default:
			usage(argv[0]);
			break;
		}
	}

	if (mag_mode == -1)
		usage(argv[0]);

	register_sig_handler();

	if (mpu9150_init(i2c_bus, sample_rate, 0))
		exit(1);

	read_loop(sample_rate);

	if (strlen(calFile) == 0) {
		if (mag_mode)
			strcpy(calFile, "magcal.txt");
		else
			strcpy(calFile, "accelcal.txt");
	}

	write_cal();

	mpu9150_exit();

	return 0;
}
示例#9
0
文件: html.c 项目: aabc/blists
int html_attachment(const char *list, unsigned int y, unsigned int m, unsigned int d, unsigned int n, unsigned int a)
{
	unsigned int aday;
	char *list_file;
	off_t idx_offset;
	int fd, error, got, trunc;
	idx_msgnum_t m1, m1r;
	struct idx_message idx_msg;
	idx_off_t offset;
	idx_size_t size;
	struct buffer src, dst;
	struct mime_ctx mime;
	char *body, *bend;

	if (y < MIN_YEAR || y > MAX_YEAR ||
	    m < 1 || m > 12 ||
	    d < 1 || d > 31 ||
	    n < 1 || n > 999999)
		return html_error("Invalid date or message number");
	aday = YMD2ADAY(y - MIN_YEAR, m, d);

	list_file = concat(MAIL_SPOOL_PATH "/", list, NULL);
	if (!list_file)
		return html_error(NULL);

	fd = idx_open(list);
	if (fd < 0) {
		error = errno;
		free(list_file);
		return html_error(error == ENOENT ?
		    "No such mailing list" : (error == ESRCH ?
		    "Index needs rebuild" : NULL));
	}

	error = !idx_read_aday_ok(fd, aday, &m1, sizeof(m1));
	if (error || m1 < 1 || m1 >= MAX_MAILBOX_MESSAGES) {
		idx_close(fd);
		free(list_file);
		return html_error((error || m1 > 0) ? NULL : "No such message");
	}
	m1r = m1 + n - 2; /* both m1 and n are 1-based; m1r is 0-based */
	idx_offset = IDX2MSG(m1r);
	got = idx_read(fd, idx_offset, &idx_msg, sizeof(idx_msg));
	if (got != sizeof(idx_msg))
		error = 1;

	if (idx_close(fd) || error) {
		free(list_file);
		return html_error(got ? NULL : "No such message");
	}

	if (y - MIN_YEAR != idx_msg.y ||
	    m != idx_msg.m ||
	    d != idx_msg.d) {
		free(list_file);
		return html_error("No such message");
	}

	offset = idx_msg.offset;
	size = idx_msg.size;

	trunc = size > MAX_MESSAGE_SIZE;
	if (trunc)
		size = MAX_MESSAGE_SIZE;
	if (buffer_init(&src, size)) {
		free(list_file);
		return html_error(NULL);
	}
	fd = open(list_file, O_RDONLY);
	free(list_file);
	if (fd < 0) {
		buffer_free(&src);
		return html_error("mbox open error");
	}
	error =
	    lseek(fd, offset, SEEK_SET) != offset ||
	    read_loop(fd, src.start, size) != size;
	if (close(fd) || error || mime_init(&mime, &src)) {
		buffer_free(&src);
		return html_error("mbox read error");
	}
	if (buffer_init(&dst, size)) {
		buffer_free(&src);
		mime_free(&mime);
		return html_error(NULL);
	}

	body = NULL;
	while (src.end - src.ptr > 9 && *src.ptr != '\n') {
		switch (*src.ptr) {
		case 'C':
		case 'c':
			mime_decode_header(&mime);
			continue;
		}
		mime_skip_header(&mime);
	}
	if (src.ptr >= src.end) {
		buffer_free(&src);
		buffer_free(&dst);
		mime_free(&mime);
		return html_error(NULL);
	}
	if (*src.ptr == '\n')
		body = ++src.ptr;

	const char *error_msg = "Attachment not found";
	unsigned int attachment_count = 0;
	if (a)
	do {
		if (mime.entities->boundary) {
			body = mime_next_body_part(&mime);
			if (!body || body >= src.end)
				break;
			body = mime_next_body(&mime);
		}
		if (mime.entities->boundary || !is_attachment(&mime) || ++attachment_count != a)
			body = NULL;
		if (!body) {
			bend = mime_skip_body(&mime);
			if (!bend)
				break;
			continue;
		}

		int text = !strncasecmp(mime.entities->type, "text/", 5);
		if (text) {
			buffer_appends(&dst, "Content-Type: text/plain");
			if (mime.entities->charset && enc_allowed_charset(mime.entities->charset))
				buffer_appendf(&dst, "; charset=%s", mime.entities->charset);
			buffer_appendc(&dst, '\n');
		} else {
			buffer_appends(&dst, "Content-Type: application/octet-stream\n");
		}
		buffer_appendf(&dst, "Content-Disposition: %s; filename=\"", text ? "inline" : "attachment");
		buffer_append_filename(&dst, mime.entities->filename, text);
		buffer_appends(&dst, "\"\n");

		body = mime_decode_body(&mime, RECODE_NO, &bend);
		if (trunc && (!body || bend >= src.end)) {
			error_msg = "Attachment is truncated";
			break;
		}

		buffer_appendf(&dst, "Content-Length: %llu\n\n", (unsigned long long)(mime.dst.ptr - body));
		buffer_append(&dst, body, mime.dst.ptr - body);
		error_msg = NULL;
		break;
	} while (bend < src.end && mime.entities);

	buffer_free(&src);

	if (error_msg || mime.dst.error || dst.error) {
		mime_free(&mime);
		buffer_free(&dst);
		return html_error(error_msg);
	}

	mime_free(&mime);

	return html_send(&dst);
}
示例#10
0
文件: html.c 项目: aabc/blists
int html_message(const char *list, unsigned int y, unsigned int m, unsigned int d, unsigned int n)
{
	unsigned int aday, n0, n2;
	char *list_file;
	off_t idx_offset;
	int fd, error, got, trunc, prev, next;
	idx_msgnum_t m0, m1, m1r;
	struct idx_message idx_msg[3];
	idx_off_t offset;
	idx_size_t size;
	struct buffer src, dst;
	struct mime_ctx mime;
	char *p, *q, *date, *from, *to, *cc, *subject, *body, *bend;

	if (y < MIN_YEAR || y > MAX_YEAR ||
	    m < 1 || m > 12 ||
	    d < 1 || d > 31 ||
	    n < 1 || n > 999999)
		return html_error("Invalid date or message number");
	aday = YMD2ADAY(y - MIN_YEAR, m, d);

	list_file = concat(MAIL_SPOOL_PATH "/", list, NULL);
	if (!list_file)
		return html_error(NULL);

	fd = idx_open(list);
	if (fd < 0) {
		error = errno;
		free(list_file);
		return html_error(error == ENOENT ?
		    "No such mailing list" : (error == ESRCH ?
		    "Index needs rebuild" : NULL));
	}

	error = !idx_read_aday_ok(fd, aday, &m1, sizeof(m1));
	if (error || m1 < 1 || m1 >= MAX_MAILBOX_MESSAGES) {
		idx_close(fd);
		free(list_file);
		return html_error((error || m1 > 0) ? NULL : "No such message");
	}
	m1r = m1 + n - (1 + 1); /* both m1 and n are 1-based; m1r is 0-based */
	idx_offset = IDX2MSG(m1r);
	prev = next = 1;
	if (m1r >= 1) {
		idx_offset -= sizeof(idx_msg[0]);
		got = idx_read(fd, idx_offset, &idx_msg, sizeof(idx_msg));
		if (got != sizeof(idx_msg)) {
			error = got != sizeof(idx_msg[0]) * 2;
			idx_msg[2] = idx_msg[1];
			next = 0;
		}
	} else {
		prev = 0;
		got = idx_read(fd, idx_offset, &idx_msg[1], sizeof(idx_msg[1]) * 2);
		if (got != sizeof(idx_msg[1]) * 2) {
			error = got != sizeof(idx_msg[1]);
			idx_msg[2] = idx_msg[1];
			next = 0;
		}
		idx_msg[0] = idx_msg[1];
	}

	n0 = n - 1;
	if (!n0 && prev && !error) {
		aday = YMD2ADAY(idx_msg[0].y, idx_msg[0].m, idx_msg[0].d);
		error = !idx_read_aday_ok(fd, aday, &m0, sizeof(m0));
		if (m1 > m0)
			n0 = m1 - m0;
		else
			error = 1;
	}

	if (idx_close(fd) || error) {
		free(list_file);
		return html_error(got ? NULL : "No such message");
	}

	n2 = n + 1;
	if (idx_msg[2].y != idx_msg[1].y ||
	    idx_msg[2].m != m || idx_msg[2].d != d)
		n2 = 1;

	if (y - MIN_YEAR != idx_msg[1].y ||
	    m != idx_msg[1].m || d != idx_msg[1].d) {
		free(list_file);
		return html_error("No such message");
	}

	offset = idx_msg[1].offset;
	size = idx_msg[1].size;

	trunc = size > MAX_MESSAGE_SIZE;
	if (trunc)
		size = MAX_MESSAGE_SIZE;
	if (buffer_init(&src, size)) {
		free(list_file);
		return html_error(NULL);
	}
	fd = open(list_file, O_RDONLY);
	free(list_file);
	if (fd < 0) {
		buffer_free(&src);
		return html_error("mbox open error");
	}
	error =
	    lseek(fd, offset, SEEK_SET) != offset ||
	    read_loop(fd, src.start, size) != size;
	if (close(fd) || error || mime_init(&mime, &src)) {
		buffer_free(&src);
		return html_error("mbox read error");
	}
	if (buffer_init(&dst, size)) {
		buffer_free(&src);
		mime_free(&mime);
		return html_error(NULL);
	}

	date = from = to = cc = subject = body = NULL;
	while (src.end - src.ptr > 9 && *src.ptr != '\n') {
		switch (*src.ptr) {
		case 'D':
		case 'd':
			if (!strncasecmp(src.ptr, "Date:", 5)) {
				date = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'F':
		case 'f':
			if (!strncasecmp(src.ptr, "From:", 5)) {
				from = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'T':
		case 't':
			if (!strncasecmp(src.ptr, "To:", 3)) {
				to = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'S':
		case 's':
			if (!strncasecmp(src.ptr, "Subject:", 8)) {
				subject = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'C':
		case 'c':
			if (!strncasecmp(src.ptr, "CC:", 3))
				cc = mime_decode_header(&mime);
			else
				mime_decode_header(&mime);
			continue;
		}
		mime_skip_header(&mime);
	}
	if (src.ptr >= src.end) {
		buffer_free(&src);
		buffer_free(&dst);
		mime_free(&mime);
		return html_error(NULL);
	}
	if (*src.ptr == '\n')
		body = ++src.ptr;

	if ((p = subject)) {
		while ((p = strchr(p, '['))) {
			if (strncasecmp(++p, list, strlen(list)))
				continue;
			q = p + strlen(list);
			if (*q != ']')
				continue;
			if (*++q == ' ') q++;
			memmove(--p, q, strlen(q) + 1);
		}
	}

	buffer_appends(&dst, "\n");

	if (html_flags & HTML_HEADER) {
		buffer_appends(&dst, "<title>");
		buffer_appends_html(&dst, list);
		if (subject && strlen(subject) > 9) {
			buffer_appends(&dst, " - ");
			buffer_appends_html(&dst, subject + 9);
		}
		buffer_appends(&dst, "</title>\n");
		if (html_flags & HTML_CENSOR)
			buffer_appends(&dst, "<meta name=\"robots\" content=\"noindex\">\n");
	}

	if (html_flags & HTML_BODY) {
		unsigned int attachment_count = 0;

		if (prev) {
			buffer_appends(&dst, "<a href=\"");
			if (n == 1)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[0].y, idx_msg[0].m, idx_msg[0].d);
			buffer_appendf(&dst, "%u\">[&lt;prev]</a> ", n0);
		}
		if (next) {
			buffer_appends(&dst, "<a href=\"");
			if (n2 == 1)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[2].y, idx_msg[2].m, idx_msg[2].d);
			buffer_appendf(&dst, "%u\">[next&gt;]</a> ", n2);
		}
		if (idx_msg[1].t.pn) {
			buffer_appends(&dst, "<a href=\"");
			if (idx_msg[1].t.py != idx_msg[1].y ||
			    idx_msg[1].t.pm != idx_msg[1].m ||
			    idx_msg[1].t.pd != idx_msg[1].d)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[1].t.py, idx_msg[1].t.pm, idx_msg[1].t.pd);
			buffer_appendf(&dst, "%u\">[&lt;thread-prev]</a> ", idx_msg[1].t.pn);
		}
		if (idx_msg[1].t.nn) {
			buffer_appends(&dst, "<a href=\"");
			if (idx_msg[1].t.ny != idx_msg[1].y ||
			    idx_msg[1].t.nm != idx_msg[1].m ||
			    idx_msg[1].t.nd != idx_msg[1].d)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[1].t.ny, idx_msg[1].t.nm, idx_msg[1].t.nd);
			buffer_appendf(&dst, "%u\">[thread-next&gt;]</a> ", idx_msg[1].t.nn);
		}
		buffer_appends(&dst,
		    "<a href=\".\">[day]</a>"
		    " <a href=\"..\">[month]</a>"
		    " <a href=\"../..\">[year]</a>"
		    " <a href=\"../../..\">[list]</a>\n");

		buffer_appends(&dst, "<pre style=\"white-space: pre-wrap\">\n");
		if (date)
			buffer_append_header(&dst, date);
		if (from)
			buffer_append_header(&dst, from);
		if (to)
			buffer_append_header(&dst, to);
		if (cc)
			buffer_append_header(&dst, cc);
		if (subject)
			buffer_append_header(&dst, subject);

		if (!(html_flags & HTML_CENSOR))
		do {
			if (mime.entities->boundary) {
				body = mime_next_body_part(&mime);
				if (!body || body >= src.end)
					break;
				body = mime_next_body(&mime);
			}
			if (mime.entities->boundary)
				body = NULL;
			if (!body) {
				bend = mime_skip_body(&mime);
				if (!bend)
					break;
				continue;
			}

			/* mime_decode_body() will break mime vars, so,
			 * remember them now */
			char *filename = mime.entities->filename;
			char *type = mime.entities->type;
			const int isattachment = is_attachment(&mime);
			const int isinline = is_inline(&mime);
			int skip = 0;

			body = mime_decode_body(&mime, isattachment ? RECODE_NO : RECODE_YES, &bend);
			if (!body)
				break;
			if (bend >= src.end)
				skip = trunc;
			bend = src.ptr;
			if (!skip && isattachment) {
				int text = !strncasecmp(type, "text/", 5);
				attachment_count++;
				buffer_appendf(&dst, "\n<span style=\"font-family: times;\"><strong>"
				    "%s attachment \"</strong><a href=\"%u/%u\"%s>",
				    text ? "View" : "Download", n, attachment_count,
				    text ? "" :  " rel=\"nofollow\" download");
				if (filename)
					buffer_appends_html(&dst, filename);
				buffer_appends(&dst, "</a><strong>\" of type \"</strong>");
				buffer_appends_html(&dst, type);
				buffer_appends(&dst, "<strong>\"");
				if (body)
					buffer_appendf(&dst, " (%llu bytes)", (unsigned long long)(mime.dst.ptr - body));
				buffer_appends(&dst, "</strong></span>\n");
				continue;
			} else if (!isinline) {
				skip = 1;
			} else {
				skip = 0; /* do not skip non-attachments */
			}
			if (skip) {
				buffer_appends(&dst, "\n<span style=\"font-family: times;\"><strong>"
				    "Content of type \"</strong>");
				buffer_appends_html(&dst, type);
				buffer_appends(&dst, "<strong>\" skipped</strong></span>\n");
				continue;
			}
			/* inline */
			buffer_appendc(&dst, '\n');
			buffer_append_html_generic(&dst, body, mime.dst.ptr - body, 0, 1);
			mime.dst.ptr = body;
		} while (bend < src.end && mime.entities);

		if ((html_flags & HTML_CENSOR) || trunc)
			buffer_appendf(&dst, "\n<span style=\"font-family: times;\"><strong>"
			    "Content %s</strong></span>\n", (html_flags & HTML_CENSOR) ? "removed" : "truncated");

		buffer_appends(&dst, "</pre>\n");
	}

	buffer_free(&src);

	if (mime.dst.error || dst.error) {
		mime_free(&mime);
		buffer_free(&dst);
		return html_error(NULL);
	}

	mime_free(&mime);

	return html_send(&dst);
}